We can take a first pass at annotating the clusters from our 10x data by comparing them to the reference dataset that we put together. This was done by compiling celltype-averaged expression values from the following single cell RNA-seq studies: (Pavličev et al. 2017; Vento-Tormo et al. 2018; Suryawanshi et al. 2018).
Data
Samples
This is the sample information sent by Alice. Not sure about the INP id for AL09 and AL10. The library for INP188 villi (associated with AL07) was problematic, so is not used.
sample.info <- read.delim("../info/from-alice/sample_info.tsv", stringsAsFactors = FALSE)
sample.info %>% kable(caption = "Sample information") %>% kable_styling(full_width = FALSE)
Sample information
| sample_id |
inp_id |
covid |
tissue |
| AL01 |
INP88 |
covid |
decidua |
| AL02 |
INP88 |
covid |
villi |
| AL03 |
INP90 |
covid |
decidua |
| AL04 |
INP90 |
covid |
villi |
| AL05 |
INP187 |
cntrl |
decidua |
| AL06 |
INP187 |
cntrl |
villi |
| AL07 |
INP188 |
cntrl |
decidua |
| AL08 |
INP188 |
cntrl |
villi |
| AL09 |
|
cntrl |
decidua |
| AL10 |
|
cntrl |
decidua |
Seurat-processed data
Read the Seurat-processed (by Eric) data sent by Alice.
seur <- readRDS("../info/from-alice/placenta.rds")
Error in deparse(attr(val, "_rs_call")) :
no slot of name "images" for this object of class "Seurat"
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
head(seur@meta.data)
The orig.ident column in the metadata represents the sample IDs from the sample info table above. We just have rename them to left-pad the numbers for consistency, and set new cluster names as default idents.
# left pad orig.idents
seur@meta.data$orig.ident <- gsub("(AL)(\\d)$", "\\10\\2", seur@meta.data$orig.ident)
# add additional metadata (covid status, tissue)
seur@meta.data$covid <- sample.info$covid[match(x = seur@meta.data$orig.ident,
table = sample.info$sample_id)]
seur@meta.data$tissue <- sample.info$tissue[match(x = seur@meta.data$orig.ident,
table = sample.info$sample_id)]
# rename clusters for consistency
seur$seurat_clusters <- paste0("clust_", seur$seurat_clusters)
seur$seurat_clusters <- gsub("(clust_)(\\d$)", "\\10\\2", as.character(seur$seurat_clusters)) # left pad
seur$seurat_clusters <- factor(seur$seurat_clusters, levels = paste0("clust_", c("00", "01", "02", "03", "04", "05", "06", "07", "08", "09", 10:34)))
# set seurat clusters as default idents
Idents(seur) <- seur@meta.data$seurat_clusters
Average by cluster
We have to get transcriptomes averaged by clusters, so that we can compare them with the reference data in order to narrow down annotations. For averaging, we will use sctransform normalized data since this is what we use for marker gene detection down the line.
# average sctransformed expression
seur.avg <- AverageExpression(seur)
# averaged data to use for correlations (SCT transformed)
plac <- seur.avg$SCT
Reference data
# read reference datasets
ref <- read.csv("../results/01_reference-atlas/vento-surya-pavli_joined.csv", stringsAsFactors = FALSE)
Get data in shape
We will join the data with the reference data so they are both in the same dataframe.
# add gene names column
plac$gene_name <- rownames(plac)
# inner join
plac <- dplyr::inner_join(plac, ref, by = "gene_name")
head(plac)
Correlations within the data
Before moving to annotating clusters by comparison with the reference dataset, first we will look at the correlation structure within the dataset.
Correlation matrix
# build correlation matrix from expression data
cor.matrix <- cor(plac %>% dplyr::select(starts_with("clust")), method = 'spearman')
# reorder correlation matrix based on clustering
dd <- as.dist((1 - cor.matrix))
hc <- hclust(dd, method = 'complete')
cormat <- cor.matrix[hc$order, hc$order]
# melt correlation matrix
dat <- reshape2::melt(cormat, na.rm = T)
## plot
p <- ggplot(data = dat, aes(Var1, Var2, fill = value)) +
geom_tile(colour = "white") +
scale_fill_gradient(low = 'white', high = 'red',
name = "Spearman\nCorrelation") +
coord_fixed(ratio = 1) +
labs(title = "Correlations among clusters") +
theme_bw() +
theme(axis.text.x = element_text(angle = 90, size = 8,
hjust=1, vjust = 0.5),
axis.text.y = element_text(size=8),
axis.ticks.length = unit(0.15, units = c('lines')),
legend.title = element_text(size = 10),
axis.title = element_blank(),
panel.grid = element_blank(),
panel.border = element_blank(),
legend.key.size = unit(0.8, units = c('lines')))
ggsave(p, filename = "../results/02_annotation/plots/corrplot_clusters.pdf",
device = "pdf", width = 7, height = 6, units = "in")
print(p)

Heirarchical clustering
# build correlation matrix from expression data
cor.matrix <- cor(plac %>% dplyr::select(starts_with("clust")), method = 'spearman')
# reorder correlation matrix based on clustering
dd <- as.dist((1 - cor.matrix))
hc <- hclust(dd, method = 'complete')
pdf(file = "../results/02_annotation/plots/hclust_clusters.pdf", width = 7, height = 5)
plot(hc, cex = 0.8)
dev.off()
null device
1
plot(hc, cex = 0.8)

There are three big cluster blocks, with substructure within them. The cluster blocks also correspond well with the UMAP plot that was sent by Alice.
Annotation by correlation
Let’s now actually measure correlations between all clusters with the reference datasets. We will treat our data as query dataset and for each cluster assign top 3 cell types in each reference dataset.
refdata <- c("vento", "surya", "pavli")
# build correlation matrix from expression data
cor.matrix <- cor(plac[, names(plac)[names(plac) != "gene_name"]],
method = 'spearman')
# reorder correlation matrix based on clustering
dd <- as.dist((1 - cor.matrix))
hc <- hclust(dd, method = 'complete')
cor.matrix <- cor.matrix[hc$order, hc$order]
# melt correlation matrix
cormat <- reshape2::melt(cor.matrix, na.rm = T)
cormat$Var1_source <- sapply(strsplit(as.character(cormat$Var1), split = "_"), "[[", 1)
cormat$Var2_source <- sapply(strsplit(as.character(cormat$Var2), split = "_"), "[[", 1)
# subset
cormat <- cormat[which(cormat$Var1_source %in% refdata &
cormat$Var2_source == "clust"), ]
# top 3 matches with highest correlation coefficients
topmatch <- data.frame( # empty df to fill top matches from the loop below
cluster = unique(as.character(cormat$Var2)) %>% sort(),
vento.top1 = NA,
surya.top1 = NA,
pavli.top1 = NA,
vento.top2 = NA,
surya.top2 = NA,
pavli.top2 = NA,
vento.top3 = NA,
surya.top3 = NA,
pavli.top3 = NA
)
cormat$top3 <- NA
for(i in unique(cormat$Var2)){
for(j in refdata){
# identify top3 match indices
ind <- which(cormat$Var2 == i & cormat$Var1_source == j)
val <- cormat$value[ind]
top3ind <- ind[order(val, decreasing = TRUE)[1:3]]
# assign match ranking to correlation data for plotting
cormat$top3[top3ind[1]] <- "1"
cormat$top3[top3ind[2]] <- "2"
cormat$top3[top3ind[3]] <- "3"
# assign top matches to topmatches dataframe
topmatch[topmatch$cluster == i, paste0(j, ".top1")] <- gsub(paste0(j, "_"), "", as.character(cormat$Var1[top3ind[1]]))
topmatch[topmatch$cluster == i, paste0(j, ".top2")] <- gsub(paste0(j, "_"), "", as.character(cormat$Var1[top3ind[2]]))
topmatch[topmatch$cluster == i, paste0(j, ".top3")] <- gsub(paste0(j, "_"), "", as.character(cormat$Var1[top3ind[3]]))
}
}
Plots
# plotting function
clustAnnoPlot <- function(dat, query, reference){
dat <- dat[which(dat$Var2_source == query & dat$Var1_source == reference), ]
p <- ggplot(data = dat,
aes(Var1, Var2, fill = value)) +
geom_tile(colour = "white") +
scale_fill_gradient(low = 'white', high = 'red',
name = "Spearman\nCorrelation") +
geom_point(aes(Var1, Var2, alpha = top3),
size = 1.5, shape = 19, stroke = 0) +
scale_alpha_manual(values = c(1, 0.5, 0.25),
breaks = c(1, 2, 3),
name = "top3", na.value = 0) +
coord_fixed(ratio = 1) +
xlab("reference") +
ylab("query") +
labs(caption = "For each celltype in query, black points represent top 3 celltypes from reference with highest correlation.",
title = paste0(query, " vs. ", reference)) +
theme_bw() +
theme(axis.text.x = element_text(angle = 90, size = 8,
hjust=1, vjust = 0.5),
axis.text.y = element_text(size=8),
axis.ticks.length = unit(0.15, units = c('lines')),
legend.title = element_text(size = 10),
axis.title = element_text(size = 8),
plot.caption = element_text(size = 7),
panel.grid = element_blank(),
panel.border = element_blank(),
legend.key.size = unit(0.8, units = c('lines')))
return(p)
}
Against Vento-Tormo et al
## Annotation plots
anno.vento <- clustAnnoPlot(dat = cormat, query = "clust", reference = "vento")
cowplot::ggsave2(anno.vento, device = "pdf", width = 7.5, height = 6.5, units = "in",
filename = "../results/02_annotation/plots/annotation-by-correlation_ref-vento.pdf")
print(anno.vento)

Against Suryavanshi et al
## Annotation plots
anno.surya <- clustAnnoPlot(dat = cormat, query = "clust", reference = "surya")
cowplot::ggsave2(anno.surya, device = "pdf", width = 7.5, height = 6.5, units = "in",
filename = "../results/02_annotation/plots/annotation-by-correlation_ref-surya.pdf")
print(anno.surya)

Against Pavlicev et al
## Annotation plots
anno.pavli <- clustAnnoPlot(dat = cormat, query = "clust", reference = "pavli")
cowplot::ggsave2(anno.pavli, device = "pdf", width = 7, height = 6, units = "in",
filename = "../results/02_annotation/plots/annotation-by-correlation_ref-pavli.pdf")
print(anno.pavli)

Top matches
topmatch %>% kable(caption = "Top 3 matches against all reference datasets.") %>% kable_styling(full_width = FALSE)
Top 3 matches against all reference datasets.
| cluster |
vento.top1 |
surya.top1 |
pavli.top1 |
vento.top2 |
surya.top2 |
pavli.top2 |
vento.top3 |
surya.top3 |
pavli.top3 |
| clust_00 |
dS3 |
dec.DSC |
DEC |
dS2 |
dec.FB1 |
ESF |
dP2 |
dec.FB2 |
SYN |
| clust_01 |
dM2 |
dec.MAC |
SYN |
dM1 |
vil.HC |
DEND |
HB |
dec.DC2 |
DEC |
| clust_02 |
dP2 |
dec.FB1 |
DEC |
dS2 |
dec.FB2 |
SYN |
dS3 |
dec.DSC |
ESF |
| clust_03 |
dM1 |
dec.MAC |
SYN |
MO |
vil.HC |
DEND |
dM2 |
dec.NK1 |
DEC |
| clust_04 |
Endo.m |
dec.VEC |
DEC |
Endo.L |
dec.LEC |
ESF |
Endo.f |
vil.VEC |
SYN |
| clust_05 |
dNK3 |
dec.NK1 |
DEC |
dNK2 |
dec.TC |
ESF |
Tcells |
dec.MAC |
SYN |
| clust_06 |
fFB1 |
dec.FB1 |
DEC |
dP2 |
vil.FB3 |
SYN |
dS2 |
dec.FB2 |
ESF |
| clust_07 |
fFB1 |
vil.FB3 |
DEC |
dP2 |
dec.FB1 |
ESF |
dS2 |
dec.FB2 |
SYN |
| clust_08 |
dM1 |
dec.MAC |
DEND |
MO |
vil.HC |
SYN |
dM2 |
dec.DC2 |
DEC |
| clust_09 |
Tcells |
dec.TC |
DEC |
dNK3 |
dec.NK1 |
ESF |
dNK2 |
dec.NK2 |
SYN |
| clust_10 |
dNK3 |
dec.NK1 |
DEC |
dNK2 |
dec.TC |
ESF |
dNK1 |
dec.NK2 |
SYN |
| clust_11 |
dM1 |
dec.MAC |
DEND |
dM2 |
vil.HC |
DEC |
dM3 |
dec.DC2 |
SYN |
| clust_12 |
dM1 |
dec.MAC |
SYN |
dM2 |
vil.HC |
DEND |
dM3 |
dec.DC2 |
DEC |
| clust_13 |
EVT |
vil.EVT |
EVT |
SCT |
dec.DSC |
DEC |
dP2 |
dec.FB1 |
ESF |
| clust_14 |
dNK3 |
dec.NK1 |
DEC |
dNK2 |
dec.TC |
ESF |
dNK1 |
dec.NK2 |
SYN |
| clust_15 |
dP2 |
dec.MAC |
DEC |
dM1 |
dec.FB2 |
SYN |
fFB1 |
dec.FB1 |
ESF |
| clust_16 |
VCT |
vil.VCT |
CYT2 |
SCT |
vil.SCT |
CYT3 |
EVT |
vil.EVT |
CYT1 |
| clust_17 |
VCT |
vil.VCT |
CYT2 |
SCT |
vil.SCT |
CYT3 |
EVT |
vil.EVT |
CYT1 |
| clust_18 |
dS3 |
dec.DSC |
DEC |
dS2 |
dec.FB1 |
ESF |
dS1 |
dec.FB2 |
CYT2 |
| clust_19 |
dP1 |
dec.SMC |
DEC |
dP2 |
vil.FB2 |
ESF |
Endo.m |
dec.FB2 |
SYN |
| clust_20 |
Plasma |
dec.MAC |
SYN |
dM1 |
dec.NK1 |
DEC |
dNK3 |
dec.TC |
DEND |
| clust_21 |
Endo.m |
dec.VEC |
DEC |
Endo.f |
vil.VEC |
SYN |
Endo.L |
dec.LEC |
ESF |
| clust_22 |
dM2 |
dec.MAC |
SYN |
dM1 |
vil.HC |
DEC |
HB |
dec.DC2 |
DEND |
| clust_23 |
SCT |
vil.VCT |
CYT2 |
VCT |
vil.SCT |
CYT3 |
EVT |
vil.EVT |
CYT1 |
| clust_24 |
SCT |
vil.SCT |
CYT2 |
VCT |
vil.VCT |
CYT3 |
EVT |
vil.EVT |
SYN |
| clust_25 |
VCT |
vil.VCT |
CYT2 |
SCT |
vil.SCT |
CYT3 |
EVT |
vil.EVT |
CYT1 |
| clust_26 |
dM1 |
dec.MAC |
DEC |
dM2 |
vil.HC |
SYN |
dM3 |
dec.VEC |
ESF |
| clust_27 |
dM2 |
dec.MAC |
SYN |
fFB1 |
vil.HC |
DEC |
HB |
vil.FB3 |
ESF |
| clust_28 |
MO |
dec.MAC |
SYN |
dM1 |
vil.HC |
CYT2 |
dM2 |
dec.NK2 |
DEND |
| clust_29 |
SCT |
vil.SCT |
CYT2 |
VCT |
vil.VCT |
CYT1 |
EVT |
vil.EVT |
CYT3 |
| clust_30 |
dNK.p |
dec.NK2 |
ESF |
dNK1 |
dec.NK1 |
DEND |
dNK3 |
dec.MAC |
CYT2 |
| clust_31 |
dM2 |
dec.MAC |
SYN |
dM1 |
vil.HC |
DEND |
MO |
dec.NK1 |
CYT2 |
| clust_32 |
Tcells |
dec.TC |
SYN |
dNK3 |
dec.NK1 |
DEC |
dM1 |
dec.MAC |
DEND |
| clust_33 |
fFB1 |
vil.FB3 |
SYN |
dP2 |
dec.SMC |
DEC |
dP1 |
dec.FB1 |
ESF |
| clust_34 |
dM2 |
dec.MAC |
SYN |
dM1 |
vil.HC |
DEND |
HB |
dec.DC2 |
DEC |
NA
Labels for clusters
We need to create consistent labels for all cell types that we identify. Below is a list I created in which the top level objects are labels we will give to the cell types, within which contained are lists of corresponding labels from vento and surya. These are tentative labels. After the first pass, when we go through the clusters with a fine-toothed comb, we can modify them further. For example, if we have multiple clusters labeled as dec.Mac (decidual macrophages), and if those clusters are distinct, we can then break up this label into dec.Mac1 and dec.Mac2.
labs <- list("dec.DSC" = list("vento.lab" = c("dS1", "dS2", "dS3"),
"surya.lab" = c("dec.DSC", "dec.FB1", "dec.FB2")),
"dec.DC" = list("vento.lab" = c("DC1", "DC2"),
"surya.lab" = c("dec.DC1", "dec.DC2")),
"dec.Mac" = list("vento.lab" = c("dM1", "dM2", "dM3"),
"surya.lab" = c("dec.MAC")),
"dec.NK" = list("vento.lab" = c("dNK.p", "dNK1", "dNK2", "dNK3", "NK.CD16neg", "NK.CD16pos"),
"surya.lab" = c("dec.NK1", "dec.NK2")),
"dec.SMC" = list("vento.lab" = c("dP1", "dP2"),
"surya.lab" = c("dec.SMC")),
"dec.Endo" = list("vento.lab" = c("Endo.m"),
"surya.lab" = c("dec.VEC")),
"dec.Epi" = list("vento.lab" = c("Epi1", "Epi2"),
"surya.lab" = c("dec.EEC")),
"dec.Tcell" = list("vento.lab" = c("Tcells"),
"surya.lab" = c("dec.TC")),
"vil.Endo" = list("vento.lab" = c("Endo.f"),
"surya.lab" = c("vil.VEC")),
"vil.EVT" = list("vento.lab" = c("EVT"),
"surya.lab" = c("vil.EVT")),
"vil.FB" = list("vento.lab" = c("fFB1", "fFB2"),
"surya.lab" = c("vil.FB1", "vil.FB2", "vil.FB3")),
"vil.Hofb" = list("vento.lab" = c("HB"),
"surya.lab" = c("vil.HC")),
"vil.SCT" = list("vento.lab" = c("SCT"),
"surya.lab" = c("vil.SCT")),
"vil.VCT" = list("vento.lab" = c("VCT"),
"surya.lab" = c("vil.VCT")),
"unk.Gran" = list("vento.lab" = c("Granulocytes"),
"surya.lab" = c()),
"unk.ILC" = list("vento.lab" = c("ILC3"),
"surya.lab" = c()),
"unk.Mono" = list("vento.lab" = c("MO"),
"surya.lab" = c()),
"unk.Plasma" = list("vento.lab" = c("Plasma"),
"surya.lab" = c()),
"unk.EB" = list("vento.lab" = c(),
"surya.lab" = c("vil.EB")),
"unk.Endo.L" = list("vento.lab" = c("Endo.L"),
"surya.lab" = c("dec.LEC"))
)
Matches consistent between reference datasets
The following clusters have consistent top1 match between vento and surya references, i.e. they correspond to the same cell type category. The pavli reference is too unresolved to be used diagnostically, so we will ignore it for now. For some cell types it is confirmatory, though, e.g. cluster_00 corresponds to DSC in all three references.
# find out which clusters are consistent.
# If vento.top1 and surya.top1 are found in the same item in the labs list, the mapping is consistent.
consistent <- c(NA)
for(i in 1:nrow(topmatch)){
consistent[i] <- grep(topmatch$vento.top1[i], labs) == grep(topmatch$surya.top1[i], labs)
}
# subset to include consistent set
topmatch.cons <- topmatch %>%
filter(consistent) %>%
dplyr::select(cluster, vento.top1, surya.top1)
# add our tentative labels to the clusters
for(i in 1:nrow(topmatch.cons)){
topmatch.cons$label[i] <- names(labs)[grep(topmatch.cons$vento.top1[i], labs)]
}
# print
topmatch.cons[order(topmatch.cons$label), ] %>% knitr::kable() %>% kable_styling(full_width = FALSE)
| |
cluster |
vento.top1 |
surya.top1 |
label |
| 1 |
clust_00 |
dS3 |
dec.DSC |
dec.DSC |
| 16 |
clust_18 |
dS3 |
dec.DSC |
dec.DSC |
| 4 |
clust_04 |
Endo.m |
dec.VEC |
dec.Endo |
| 18 |
clust_21 |
Endo.m |
dec.VEC |
dec.Endo |
| 2 |
clust_01 |
dM2 |
dec.MAC |
dec.Mac |
| 3 |
clust_03 |
dM1 |
dec.MAC |
dec.Mac |
| 7 |
clust_08 |
dM1 |
dec.MAC |
dec.Mac |
| 10 |
clust_11 |
dM1 |
dec.MAC |
dec.Mac |
| 11 |
clust_12 |
dM1 |
dec.MAC |
dec.Mac |
| 19 |
clust_22 |
dM2 |
dec.MAC |
dec.Mac |
| 22 |
clust_26 |
dM1 |
dec.MAC |
dec.Mac |
| 23 |
clust_27 |
dM2 |
dec.MAC |
dec.Mac |
| 26 |
clust_31 |
dM2 |
dec.MAC |
dec.Mac |
| 29 |
clust_34 |
dM2 |
dec.MAC |
dec.Mac |
| 5 |
clust_05 |
dNK3 |
dec.NK1 |
dec.NK |
| 9 |
clust_10 |
dNK3 |
dec.NK1 |
dec.NK |
| 13 |
clust_14 |
dNK3 |
dec.NK1 |
dec.NK |
| 25 |
clust_30 |
dNK.p |
dec.NK2 |
dec.NK |
| 17 |
clust_19 |
dP1 |
dec.SMC |
dec.SMC |
| 8 |
clust_09 |
Tcells |
dec.TC |
dec.Tcell |
| 27 |
clust_32 |
Tcells |
dec.TC |
dec.Tcell |
| 12 |
clust_13 |
EVT |
vil.EVT |
vil.EVT |
| 6 |
clust_07 |
fFB1 |
vil.FB3 |
vil.FB |
| 28 |
clust_33 |
fFB1 |
vil.FB3 |
vil.FB |
| 20 |
clust_24 |
SCT |
vil.SCT |
vil.SCT |
| 24 |
clust_29 |
SCT |
vil.SCT |
vil.SCT |
| 14 |
clust_16 |
VCT |
vil.VCT |
vil.VCT |
| 15 |
clust_17 |
VCT |
vil.VCT |
vil.VCT |
| 21 |
clust_25 |
VCT |
vil.VCT |
vil.VCT |
Ambiguous clusters
The top1 matches for some clusters with vento and surya are not the same. The are the clusters that will require a more detailed look to determine their identify.
# subset for ambiguous clusters
topmatch.ambig <- topmatch %>%
filter(!consistent) %>%
dplyr::select(cluster, vento.top1, surya.top1)
# add our labels.
for(i in 1:nrow(topmatch.ambig)){
topmatch.ambig$label[i] <- paste0(
names(labs)[grep(topmatch.ambig$vento.top1[i], labs)],
" or ",
names(labs)[grep(topmatch.ambig$surya.top1[i], labs)]
)
}
# print
topmatch.ambig[order(topmatch.ambig$label), ] %>% kable() %>% kable_styling(full_width = FALSE)
| |
cluster |
vento.top1 |
surya.top1 |
label |
| 1 |
clust_02 |
dP2 |
dec.FB1 |
dec.SMC or dec.DSC |
| 3 |
clust_15 |
dP2 |
dec.MAC |
dec.SMC or dec.Mac |
| 6 |
clust_28 |
MO |
dec.MAC |
unk.Mono or dec.Mac |
| 4 |
clust_20 |
Plasma |
dec.MAC |
unk.Plasma or dec.Mac |
| 2 |
clust_06 |
fFB1 |
dec.FB1 |
vil.FB or dec.DSC |
| 5 |
clust_23 |
SCT |
vil.VCT |
vil.SCT or vil.VCT |
Annotation refinement
The annotations we have so far are only a starting point. Now we can look at each annotation more carefully to make sure that everything makes sense.
Sample contribution to clusters
One of the ways in which we can refine the clusters further is by knowing which tissue that sample arises from. For example, if a cluster has macrophage gene signature and if most cells in that cluster come from villi samples, we can further narrow down the identity of the cluster to Hofbauer cells. We can do this by simply counting for each cluster how many cells come from decidua vs villi and by calculating the percentage.
# count cells in each cluster by tissue of origin
bytissue <- table(seur$tissue, seur$seurat_clusters) %>%
as.data.frame() %>%
dplyr::rename(tissue = Var1, cluster = Var2, frequency = Freq)
# calculate fraction
for(i in 1:nrow(bytissue)){
bytissue$fraction[i] <- bytissue$frequency[i]/
sum(bytissue$frequency[bytissue$cluster == bytissue$cluster[i]])
}
# plot
p.cells <- ggplot(data = bytissue, aes(x = frequency, y = cluster)) +
geom_bar(aes(fill = tissue), stat = "identity", position = "stack") +
ggtitle("Number of cells")
p.frac <- ggplot(data = bytissue, aes(x = fraction, y = cluster)) +
geom_bar(aes(fill = tissue), stat = "identity", position = "stack") +
ggtitle("Fraction of cells")
p.cells + p.frac + plot_layout(guides = "collect", nrow = 2) +
plot_annotation(caption = "Sample contribution to clusters") &
coord_flip() &
scale_fill_tableau(palette = "Classic 10 Medium") &
theme_minimal() +
theme(panel.grid.minor = element_blank(),
panel.grid.major = element_line(size = 0.25),
plot.title = element_text(size = 10),
axis.text.x = element_text(angle = 90, vjust = 0.5))

NA
NA
Following clusters have more than 75% cells coming from the same tissue of origin.
bytissue[, c("cluster", "tissue", "fraction")] %>%
pivot_wider(names_from = "tissue", values_from = "fraction") %>%
filter(decidua > 0.75 | villi > 0.75) %>%
kable(digits = 2, row.names = FALSE,
caption = "Clusters with more than 75% cells from one tissue") %>%
kable_styling(full_width = FALSE)
Clusters with more than 75% cells from one tissue
| cluster |
decidua |
villi |
| clust_00 |
0.98 |
0.02 |
| clust_01 |
0.16 |
0.84 |
| clust_04 |
0.87 |
0.13 |
| clust_05 |
0.94 |
0.06 |
| clust_06 |
0.24 |
0.76 |
| clust_12 |
0.82 |
0.18 |
| clust_13 |
0.93 |
0.07 |
| clust_15 |
0.78 |
0.22 |
| clust_16 |
0.87 |
0.13 |
| clust_17 |
0.99 |
0.01 |
| clust_18 |
0.92 |
0.08 |
| clust_19 |
0.77 |
0.23 |
| clust_21 |
0.76 |
0.24 |
| clust_22 |
0.12 |
0.88 |
| clust_24 |
0.77 |
0.23 |
| clust_25 |
0.85 |
0.15 |
| clust_26 |
0.22 |
0.78 |
| clust_27 |
0.02 |
0.98 |
| clust_29 |
0.79 |
0.21 |
| clust_31 |
0.02 |
0.98 |
| clust_33 |
0.04 |
0.96 |
| clust_34 |
0.03 |
0.97 |
Following clusters have ambiguous origin, i.e. they contain cells from decidua and villi samples.
bytissue[, c("cluster", "tissue", "fraction")] %>%
pivot_wider(names_from = "tissue", values_from = "fraction") %>%
filter(decidua < 0.75 & villi < 0.75) %>%
kable(digits = 2, row.names = FALSE,
caption = "Clusters with cells from both tissues") %>%
kable_styling(full_width = FALSE)
Clusters with cells from both tissues
| cluster |
decidua |
villi |
| clust_02 |
0.65 |
0.35 |
| clust_03 |
0.55 |
0.45 |
| clust_07 |
0.29 |
0.71 |
| clust_08 |
0.38 |
0.62 |
| clust_09 |
0.57 |
0.43 |
| clust_10 |
0.59 |
0.41 |
| clust_11 |
0.44 |
0.56 |
| clust_14 |
0.67 |
0.33 |
| clust_20 |
0.44 |
0.56 |
| clust_23 |
0.74 |
0.26 |
| clust_28 |
0.66 |
0.34 |
| clust_30 |
0.69 |
0.31 |
| clust_32 |
0.41 |
0.59 |
This doesn’t necessarily clarify too many things. There are many clusters that have over 3/4th of the cells coming from the same tissue, but often not the tissue you expect. For example clusters 16 and 17 are either villous or syncytial trophoblast cells but they are coming mostly from decidua samples. You expect that for cluster 13, which is extravillous trophoblast but not for the other types. At least in the species that I have worked with, it is often difficult to separate the fetal tissue from maternal tissue. So this mixed result could be a result of that difficulty. It looks like we can use this a rough guide, but we can’t necessarily rely of this analysis, i.e. the marker gene should take precedence over tissue of origin in cell type id because the latter is experimentally (and as a consequence, analytically) messy to disentangle.
Marker genes
Identify marker genes
I ran Seurat::findAllMarkers on this data using the SCT assay. This function outputs a list of marker genes for each cluster compared to all other cells in the data. This takes a while (hours) to run, so I ran it on the cluster and saved the results to a .csv. See the code directory for full code, and see below for the function call.
# set default assay to SCT
DefaultAssay(object = plac) <- "SCT"
# find markers for all clusters
markers <- FindAllMarkers(object = plac,
only.pos = TRUE,
logfc.threshold = 0.25)
# write.csv
write.csv(markers, "/home/arc78/scratch60/covid-placenta/markers_sct.csv", row.names = FALSE)
markers <- read.csv("../results/02_annotation/files/markers_sct.csv")
# rename clusters
markers$cluster <- paste0("clust_", markers$cluster)
markers$cluster <- gsub("(clust_)(\\d)$", "\\10\\2", markers$cluster)
markers$cluster <- factor(markers$cluster, levels = unique(markers$cluster))
# split by cluster
markers <- split.data.frame(markers, markers$cluster)
Plot marker genes
We will plot top 50 marker genes for all clusters and save the plot to pdf.
# plotting function
DotPlot2 <- function(object = "seur", assay = "SCT", features, title, ...) {
p <- DotPlot(seur,
assay = "SCT",
features = features,
dot.min = 0.05, dot.scale = 4) +
coord_flip() +
labs(caption = paste0(assay, " normalized expression"), title = title) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1),
panel.grid = element_blank())
return(p)
}
top50marker.plots <- list()
for(i in names(markers)) {
if(nrow(markers[[i]]) > 50)
features <- markers[[i]]$gene[1:50]
if(nrow(markers[[i]]) < 50)
features <- markers[[i]]$gene
top50marker.plots[[i]] <- DotPlot2(features = features, title = i)
cowplot::ggsave2(top50marker.plots[[i]],
filename = paste0("../results/02_annotation/plots/top50markers_dotplot_", i, ".pdf"),
width = 7.5, height = 7.5, units = "in")
}
Manual annotation
With the marker gene plots we have made, we can examine the top marker genes in each cluster to manually confirm or adjust the annotation of each cluster.
Let’s make a dataframe in which to keep track of the assignments.
annotation <- data.frame(
cluster = unique(seur$seurat_clusters) %>% sort(),
annotation = NA,
notes = NA
)
For each set of marker genes, we can also perform a number of manual checks to assess cell type annotation.
First is GO enrichment using clusterProfile package, for which below is a function for quickly looking at top 10 enriched GO categories.
# function for GO enrichment
# 1. for a given cluster, prints n.print.rows top enriched GO terms
# 2. Can exclude ribosomal genes (default) because they dominate some GO enrichments
enrichGO2 <- function(clust, ngenes = 100, include.ribo = FALSE, n.print.rows = 10, ...) {
ids.bitr <- bitr(markers[[clust]]$gene[1:ngenes],
fromType = "SYMBOL", toType = c("ENTREZID", "SYMBOL"),
OrgDb = "org.Hs.eg.db")
if(include.ribo == FALSE)
features <- ids.bitr$ENTREZID[!(grepl("RP[SL]", ids.bitr$SYMBOL))]
if(include.ribo == TRUE)
features <- ids.bitr$ENTREZID
ego <- enrichGO(
gene = features,
OrgDb = org.Hs.eg.db,
ont = "BP",
pAdjustMethod = "BH",
pvalueCutoff = 0.01, # default 0.01
qvalueCutoff = 0.05, # default 0.05
readable = TRUE)
dftoprint <- clusterProfiler::simplify(ego)@result[1:n.print.rows, c(2,8)]
rownames(dftoprint) <- NULL
return(dftoprint)
}
We can also check if the cluster is enriched is genes that are labeled as markers genes for a cell type by other studies. (Vento-Tormo et al. 2018) have provided top 30 marker genes for all cell types identified by them.
# read top 30 markers from vento-tormo et al
markers.vento <- readxl::read_excel("../info/from-papers/vento-tormo_2018/41586_2018_698_MOESM1_ESM/Supplementary Table 2.xlsx")
# rename columns
markers.vento <- dplyr::rename(markers.vento,
VCT_1 = VCT...2,
Tcells_1 = `T cells...5`,
Tcells_2 = `T cells...9`,
VCT_2 = `VCT...10`,
Tcells_3 = `T cells...11`,
FB_1 = F1,
EVT_1 = `EVT...14`,
Tcells_4 = `T cells...15`,
EVT_2 = `EVT...16`,
NKCD16pos = `Blood NK CD16+`,
MO_1 = `MO...22`,
MO_2 = `MO...27`,
NKCD16neg = `Blood NK CD16-`,
FB2 = F2,
Endo.m = `Endo (m)`,
Endo.L = `Endo L`,
Endo.f = `Endo (f)`
)
# remove ensembl ids
markers.vento <- apply(X = markers.vento, MARGIN = 2, FUN = function(x) gsub("_ENSG\\d+", "", x)) %>%
as.data.frame()
(Suryawanshi et al. 2018) have also provided a list of top30 marker genes in the supplementary data.
markers.surya.vil <- readxl::read_excel("../info/from-papers/suryawanshi_2018/supp-data/aau4788_Data_file_S3.xlsx", sheet = "Villi", range = "A1:G271", trim_ws = TRUE)
markers.surya.dec <- readxl::read_excel("../info/from-papers/suryawanshi_2018/supp-data/aau4788_Data_file_S3.xlsx", sheet = "Deciuda", range = "A1:G331", trim_ws = TRUE)
markers.surya.vil <- dplyr::rename(markers.surya.vil, cluster = `cell type`)
markers.surya.vil$cluster <- paste0("vil.", markers.surya.vil$cluster)
markers.surya.dec$`cluster` <- paste0("dec.", markers.surya.dec$`cluster`)
markers.surya <- rbind(markers.surya.vil, markers.surya.dec)
Clusters 0 and 18
Notes
- Clusters 0 and 18 highly correlated with each other and most similar to each other than to other clusters.
- Both are consistently similar to the decidual stromal cells in both vento and surya datasets.
- The marker genes include Prolactin, IGFBP1 etc, which are typical of decidual stromal cells.
- These two clusters also arise unabiguously from the decidua samples rather than villi samples.
annotation$annotation[annotation$cluster %in% c("clust_00", "clust_18")] <- "dec.DSC"
annotation[annotation$cluster %in% paste0("clust_", c("00", "18")), ]
Marker genes plots
top50marker.plots[["clust_00"]]

top50marker.plots[["clust_18"]]

Go anrichment
enrichGO2("clust_00")
'select()' returned 1:1 mapping between keys and columns
Vento markers for DSC
DotPlot2(features = markers.vento$dS3, title = "vento: dS3")

Surya markers for DSC
DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.DSC"], title = "surya markers: DSC")

Clusters 4 and 21
Notes
- Clusters 4 and 21 are highly correlated.
- They consistently get assigned as decidual endothelial cells by both vento and surya datasets.
- More than 75% of the cells in both of these clusters come from the decidua samples.
- Markers genes of both clusters are enriched in GO categories related to epithelium development, endothelium development etc.
- But these two clusters are not as alike as clusters 0 and 18. They are different in terms of their expression of many endothelium related genes like CD34, PROX1, VWF, SPARCL. Cluster 21 looks more like “typical” endothelial cells. Cluster 4 has low expression of many typical endothelial genes.
- Cluster 4 has higher expression of some lymphatic endothelial cells: PROX1, LYVE1. Many genes from the list of surya marker genes for dec.LEC are expressed in cluster 4. This suggests that Cluster 4 may be lymphatic endothelial cells and cluster 21 may be endothelial cells.
annotation$annotation[annotation$cluster %in% c("clust_04")] <- "dec.Endo.L"
annotation$annotation[annotation$cluster %in% c("clust_21")] <- "dec.Endo"
annotation[annotation$cluster %in% paste0("clust_", c("04", "21")), ]
Marker gene plots
top50marker.plots[["clust_04"]]

top50marker.plots[["clust_21"]]

GO enrichment
enrichGO2("clust_04")
'select()' returned 1:1 mapping between keys and columns
2% of input gene IDs are fail to map...
enrichGO2("clust_21")
'select()' returned 1:1 mapping between keys and columns
2% of input gene IDs are fail to map...
Vento markers for endothelial cells
DotPlot2(features = markers.vento$Endo.m, title = "vento: Endo.m")

DotPlot2(features = markers.vento$Endo.f, title = "vento: Endo.f")

DotPlot2(features = markers.vento$Endo.L, title = "vento: Endo.L")

Surya markers for endothelial cells
DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.VEC"], title = "surya markers: vil.VEC")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.VEC"], title = "surya markers: dec.VEC")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.LEC"], title = "surya markers: dec.LEC")

Cluster 13
Notes
- This cluster is unique in its expression of HLA-G, typical of extravillous trophoblast.
- Several other genes often enriched in EVT are also enriched in this cluster: FN1, PAPPA2, DIO2, etc.
- top 30 markers associated with EVT_2 from vento dataset are also highly and specifically expressed in this cluster.
- It is consistently assigned to be EVT based on vento, surya, and pavli datasets.
Annotation:
Cluster 13: Extravillous trophoblast (vil.EVT)
annotation$annotation[annotation$cluster %in% c("clust_13")] <- "vil.EVT"
annotation[annotation$cluster %in% c("clust_13"), ]
Marker genes plot
top50marker.plots[["clust_13"]]

GO enrichment
enrichGO2("clust_13")
1% of input gene IDs are fail to map...
Vento markers for EVT
DotPlot2(features = markers.vento$EVT_1, title = "vento markers: EVT_1")

DotPlot2(features = markers.vento$EVT_2, title = "vento markers: EVT_2")

Surya marker genes for EVT
DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.EVT"], title = "surya markers: vil.EVT")

Other trophoblast clusters (16, 17, 23, 24, 25, 29)
Notes
For more information on expression patterns and markers of different trophoblast types, see (Liu et al. 2018).
- In addition to cluster 13 (EVT), there are 6 other clusters that are likely trophoblast cell types (they express KRT7 and are related clusters). These 6 clusters are organized into two correlated blocks (see “correlations section above”): (16, 17, 25) and (23, 29, 24) of which the former are likely VCT and the latter SCT according to correlation-based annotation.
- Cluster 29 appears to be SCT given its expression of CGA, GDF15, ERVFRD-1 (Syncytin 2) genes. For some reason GO enrichment didn’t work for markers of cluster 29, but it does consistently express SCT markers from vento and surya.
- Cluster 24 doesn’t express ERVFRD-1 much, but it does express most other telltale genes of SCT: CSH1, CSH2, CGA, CSHL1, many PSG genes, GH2, PGF, GDF15, etc. And it is not enriched for expression of VCT markers from vento and surya. It’s enriched GO terms have to do with hormone production and signaling as expected for SCT. Thus, this is most likely an SCT cluster.
- Clusters 16 and 17 don’t express many markers of SCT, but do express many vento and surya markers of VCT: PAGE4, PEG10, XAGE3, etc.
- Cluster 25 has VCT markers as well as a strong signature of cell division genes. This shows up in the GO enrichment as well, where most enriched GO terms are “nuclear division”, “chromosome segregation”, “cell cycle G1/S phase transition” etc. This is likely a cluster of proliferating VCT cells. Incidentally both (Vento-Tormo et al. 2018) and (Liu et al. 2018) have a separate population of VCT with a cell cycle gene signature (VCT_2 in vento).
- Cluster 23 is most similar to SCT of vento but VCT of surya. Indeed this cluster has expression of marker genes for both VCT and SCT (see plots). It expresses PAGE4, PEG10 etc, but also expresses syncytin gene ERVW-1. This could represent a group of cytotrophoblast cells that are in the process of differentiating to sycnytial trophoblast. We will label it as a VCT type.
annotation$annotation[annotation$cluster %in% c("clust_16")] <- "vil.VCT_1"
annotation$annotation[annotation$cluster %in% c("clust_17")] <- "vil.VCT_2"
annotation$annotation[annotation$cluster %in% c("clust_23")] <- "vil.VCT_3"
annotation$annotation[annotation$cluster %in% c("clust_25")] <- "vil.VCT_4"
annotation$annotation[annotation$cluster %in% c("clust_24")] <- "vil.SCT_1"
annotation$annotation[annotation$cluster %in% c("clust_29")] <- "vil.SCT_2"
annotation$notes[annotation$cluster %in% c("clust_25")] <- "proliferating"
annotation[annotation$cluster %in% paste0("clust_", c("16", "17", "23", "25", "24", "29")), ]
Marker gene plots
top50marker.plots[["clust_16"]]

top50marker.plots[["clust_17"]]

top50marker.plots[["clust_25"]]

top50marker.plots[["clust_23"]]

top50marker.plots[["clust_24"]]

top50marker.plots[["clust_29"]]

GO enrichment
enrichGO2("clust_16")
enrichGO2("clust_17")
2.5% of input gene IDs are fail to map...
enrichGO2("clust_25")
3% of input gene IDs are fail to map...
enrichGO2("clust_23")
enrichGO2("clust_24")
1% of input gene IDs are fail to map...
enrichGO2("clust_29")
2% of input gene IDs are fail to map...
Vento markers for VCT and SCT
DotPlot2(features = markers.vento$VCT_1, title = "vento markers: VCT_1")

DotPlot2(features = markers.vento$VCT_2, title = "vento markers: VCT_2")

DotPlot2(features = markers.vento$SCT, title = "vento markers: SCT")

Surya markers for VCT and SCT
DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.VCT"], title = "surya markers: vil.VCT")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.SCT"], title = "surya markers: vil.SCT")

Pavli marker genes for VCT and SCT
DotPlot2(features = c("ADRB1", "PUF60", "SNORDA3A", "PRMT7", "E1F1AY",
"FXYD3", "GRAMD2", "INSL4", "ITGB8", "PAGE4",
"SLC13A4", "SLC22A11"),
title = "pavli markers: VCT_1")

DotPlot2(features = c("XAGE3", "XAGE2", "SERINC5", "S100P", "RASA1",
"MFAP5", "LIN28B", "KRT8", "KRT7", "INS-IGF2", "GCM1",
"GATA3", "ERVW-1", "ERVFRD-1", "EGR1", "EFNA1",
"CYP19A1", "PHLDA2", "ACKR2"),
title = "pavli markers: VCT_2")

DotPlot2(features = c("SEMA3B", "CSH2", "CSHL1", "FCGR2A", "GH2", "HBA1",
"HBA2", "HBB", "HBG1", "HBG2", "HLA-DMA", "HLA-DPA1",
"HLA-DQB1", "HLA-DRA", "HLA-DRB1", "HPGDS", "CGB8",
"LGALS14", "LYVE1", "NPIPB3", "CGB5", "PSG1", "PSG3",
"PSG6", "PSG9", "ZNF117", "ZNF91", "HIST2H2AB",
"RAMP2", "MUC20"),
title = "pavli markers: SCT")

Liu et al markers for trophoblasts
DotPlot2(seur, features = c("GCM1", "CSH1", "KRT7", "TFAP2C", "GATA3", "CDH1", "EGFR", "HLA-G", "MMP2", "RRM2", "CCNB1", "CDK1", "ERVFRD-1", "SLC1A5", "PAGE4", "CGB"), title = "Genes from Liu et al 2018")
The following requested variables were not found: CGB

Clusters 02, 06, 07, 15, 19, 33
Notes
- Most of these clusters represent some kind of fibroblast or a closely related cell type. Most clusters are enriched for GO terms related to ECM regulation.
- Clusters 02 and 15 marker genes are also highly expressed in clusters 00 and 18. Cluster 15 has more than 75% cells coming from decidua samples, and cluster 02 has over 50% cells coming from decidua samples. Decidual stromal cells are known to be actually a few different populations. Even in Vento-tormo et al, they are labeled as dS1–3, of which dS3 are the canonical DSC with PRL and IGFBP1, while dS1 and dS2 are closely related to DSC and are derived from endometrial stromal fibroblasts. These things together suggest that clusters 02 and 15 are the non-PRL varieties of DSC. To distinguish them from DSC, we can call them decidual fibroblasts.
- Marker genes of 06, 07, and 33 show highly correlated gene expressin, i.e. the markers on each of these clusters are also highly expressed in the other two clusters, suggesting that these three clusters a closely related cell populations. All three of these clusters are largely (over 75% of the cells) derived from villi samples, and they are fibroblast-like cells. Thus, these are likely villous fibroblasts.
- Cluster 19 is most similar to PV (perivascular) clusters from vento and SMC (smooth muscle cell) cluster from surya. Indeed, its markers include smooth muscle genes like MGP, MYH11, MYL9 etc, and enriched GO terms for this cluster include “muscle contraction” and “artery morphogenesis”. Thus, this cluster is most likely of perivascular smooth muscle cells of decidual origin (over 75% cells from decidua samples).
annotation$annotation[annotation$cluster %in% c("clust_02")] <- "dec.FB_1"
annotation$annotation[annotation$cluster %in% c("clust_15")] <- "dec.FB_2"
annotation$annotation[annotation$cluster %in% c("clust_06")] <- "vil.FB_1"
annotation$annotation[annotation$cluster %in% c("clust_07")] <- "vil.FB_2"
annotation$annotation[annotation$cluster %in% c("clust_33")] <- "vil.FB_3"
annotation$annotation[annotation$cluster %in% c("clust_19")] <- "dec.SMC"
annotation[annotation$cluster %in% paste0("clust_", c("02", "15", "06", "07", "33", "19")), ]
Marker gene plots
top50marker.plots[["clust_02"]]

top50marker.plots[["clust_15"]]

top50marker.plots[["clust_06"]]

top50marker.plots[["clust_07"]]

top50marker.plots[["clust_19"]]

top50marker.plots[["clust_33"]]

GO enrichment
enrichGO2("clust_02")
10.77% of input gene IDs are fail to map...
enrichGO2("clust_15")
2.56% of input gene IDs are fail to map...
enrichGO2("clust_06")
1% of input gene IDs are fail to map...
enrichGO2("clust_07")
3% of input gene IDs are fail to map...
enrichGO2("clust_19")
2% of input gene IDs are fail to map...
enrichGO2("clust_33")
3% of input gene IDs are fail to map...
Vento markers
DotPlot2(features = markers.vento$PV1, title = "vento markers: PV1")

DotPlot2(features = markers.vento$PV2, title = "vento markers: PV2")

Surya markers
DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.FB1"], title = "surya markers: vil.FB1")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.SMC"], title = "surya markers: dec.SMC")

Cluster 20
Notes
This is most likely a cluster of B cells. Most markers genes are related to B cells: many immunoglobulin gnees, SPIB, MS4A1, CD79A, CD79B, BANK1, etc. It also expresses many genes that are markers for Plasma cluster from vento dataset. GO enrichment also agrees. Even though about half the cells from the cluster come from decidua and villi each, the likely origin of B cells is maternal rather than fetal.
annotation$annotation[annotation$cluster %in% c("clust_20")] <- "dec.Bcells"
annotation[annotation$cluster %in% paste0("clust_", c("20")), ]
Marker genes plot
top50marker.plots[["clust_20"]]

GO enrichment
enrichGO2("clust_20")
1% of input gene IDs are fail to map...
Vento markers
DotPlot2(features = markers.vento$Plasma, title = "vento markers: Plasma")

LS0tCnRpdGxlOiAiQW5ub3RhdGlvbiBvZiBjbHVzdGVycyBpbiAxMHggZGF0YSIKYXV0aG9yOiAiQXJ1biBDaGF2YW4iCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKYmlibGlvZ3JhcGh5OiAuLi9yZWZzLmJpYgotLS0KU3RhcnRlZDogMjAyMC0wOS0wMiAgCkxhc3QgZWRpdGVkOiBgciBmb3JtYXQoU3lzLnRpbWUoKSlgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyMgcGFja2FnZXMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmxpYnJhcnkodGlkeXZlcnNlKQoKIyBzaW5nbGUgY2VsbApsaWJyYXJ5KFNldXJhdCkKCiMgcm1kCmxpYnJhcnkoa2FibGVFeHRyYSkKCiMgcGxvdHRpbmcKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZ2d0aGVtZXMpCgojIGdvIGVucmljaG1lbnQKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpgYGAKCldlIGNhbiB0YWtlIGEgZmlyc3QgcGFzcyBhdCBhbm5vdGF0aW5nIHRoZSBjbHVzdGVycyBmcm9tIG91ciAxMHggZGF0YSBieSBjb21wYXJpbmcgdGhlbSB0byB0aGUgcmVmZXJlbmNlIGRhdGFzZXQgdGhhdCB3ZSBwdXQgdG9nZXRoZXIuIFRoaXMgd2FzIGRvbmUgYnkgY29tcGlsaW5nIGNlbGx0eXBlLWF2ZXJhZ2VkIGV4cHJlc3Npb24gdmFsdWVzIGZyb20gdGhlIGZvbGxvd2luZyBzaW5nbGUgY2VsbCBSTkEtc2VxIHN0dWRpZXM6IFtAcGF2bGljZXZfc2luZ2xlLWNlbGxfMjAxNzsgQHZlbnRvLXRvcm1vX3NpbmdsZS1jZWxsXzIwMTg7IEBzdXJ5YXdhbnNoaV9zaW5nbGUtY2VsbF8yMDE4XS4gCgojIERhdGEKCiMjIFNhbXBsZXMKVGhpcyBpcyB0aGUgc2FtcGxlIGluZm9ybWF0aW9uIHNlbnQgYnkgQWxpY2UuIE5vdCBzdXJlIGFib3V0IHRoZSBJTlAgaWQgZm9yIGBBTDA5YCBhbmQgYEFMMTBgLiBUaGUgbGlicmFyeSBmb3IgYElOUDE4OGAgdmlsbGkgKGFzc29jaWF0ZWQgd2l0aCBgQUwwN2ApIHdhcyBwcm9ibGVtYXRpYywgc28gaXMgbm90IHVzZWQuICAKCmBgYHtyfQpzYW1wbGUuaW5mbyA8LSByZWFkLmRlbGltKCIuLi9pbmZvL2Zyb20tYWxpY2Uvc2FtcGxlX2luZm8udHN2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpzYW1wbGUuaW5mbyAlPiUga2FibGUoY2FwdGlvbiA9ICJTYW1wbGUgaW5mb3JtYXRpb24iKSAlPiUga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKIyMgU2V1cmF0LXByb2Nlc3NlZCBkYXRhClJlYWQgdGhlIGBTZXVyYXRgLXByb2Nlc3NlZCAoYnkgRXJpYykgZGF0YSBzZW50IGJ5IEFsaWNlLiAgCgpgYGB7cn0Kc2V1ciA8LSByZWFkUkRTKCIuLi9pbmZvL2Zyb20tYWxpY2UvcGxhY2VudGEucmRzIikKCmhlYWQoc2V1ckBtZXRhLmRhdGEpCmBgYAoKVGhlIGBvcmlnLmlkZW50YCBjb2x1bW4gaW4gdGhlIG1ldGFkYXRhIHJlcHJlc2VudHMgdGhlIHNhbXBsZSBJRHMgZnJvbSB0aGUgc2FtcGxlIGluZm8gdGFibGUgYWJvdmUuIFdlIGp1c3QgaGF2ZSByZW5hbWUgdGhlbSB0byBsZWZ0LXBhZCB0aGUgbnVtYmVycyBmb3IgY29uc2lzdGVuY3ksIGFuZCBzZXQgbmV3IGNsdXN0ZXIgbmFtZXMgYXMgZGVmYXVsdCBpZGVudHMuCgpgYGB7cn0KIyBsZWZ0IHBhZCBvcmlnLmlkZW50cwpzZXVyQG1ldGEuZGF0YSRvcmlnLmlkZW50IDwtIGdzdWIoIihBTCkoXFxkKSQiLCAiXFwxMFxcMiIsIHNldXJAbWV0YS5kYXRhJG9yaWcuaWRlbnQpCgojIGFkZCBhZGRpdGlvbmFsIG1ldGFkYXRhIChjb3ZpZCBzdGF0dXMsIHRpc3N1ZSkKc2V1ckBtZXRhLmRhdGEkY292aWQgPC0gc2FtcGxlLmluZm8kY292aWRbbWF0Y2goeCA9IHNldXJAbWV0YS5kYXRhJG9yaWcuaWRlbnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YWJsZSA9IHNhbXBsZS5pbmZvJHNhbXBsZV9pZCldCnNldXJAbWV0YS5kYXRhJHRpc3N1ZSA8LSBzYW1wbGUuaW5mbyR0aXNzdWVbbWF0Y2goeCA9IHNldXJAbWV0YS5kYXRhJG9yaWcuaWRlbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFibGUgPSBzYW1wbGUuaW5mbyRzYW1wbGVfaWQpXQoKIyByZW5hbWUgY2x1c3RlcnMgZm9yIGNvbnNpc3RlbmN5CnNldXIkc2V1cmF0X2NsdXN0ZXJzIDwtIHBhc3RlMCgiY2x1c3RfIiwgc2V1ciRzZXVyYXRfY2x1c3RlcnMpCnNldXIkc2V1cmF0X2NsdXN0ZXJzIDwtIGdzdWIoIihjbHVzdF8pKFxcZCQpIiwgIlxcMTBcXDIiLCBhcy5jaGFyYWN0ZXIoc2V1ciRzZXVyYXRfY2x1c3RlcnMpKSAjIGxlZnQgcGFkCnNldXIkc2V1cmF0X2NsdXN0ZXJzIDwtIGZhY3RvcihzZXVyJHNldXJhdF9jbHVzdGVycywgbGV2ZWxzID0gcGFzdGUwKCJjbHVzdF8iLCBjKCIwMCIsICIwMSIsICIwMiIsICIwMyIsICIwNCIsICIwNSIsICIwNiIsICIwNyIsICIwOCIsICIwOSIsIDEwOjM0KSkpCgojIHNldCBzZXVyYXQgY2x1c3RlcnMgYXMgZGVmYXVsdCBpZGVudHMKSWRlbnRzKHNldXIpIDwtIHNldXJAbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycwpgYGAKCiMjIEF2ZXJhZ2UgYnkgY2x1c3RlcgpXZSBoYXZlIHRvIGdldCB0cmFuc2NyaXB0b21lcyBhdmVyYWdlZCBieSBjbHVzdGVycywgc28gdGhhdCB3ZSBjYW4gY29tcGFyZSB0aGVtIHdpdGggdGhlIHJlZmVyZW5jZSBkYXRhIGluIG9yZGVyIHRvIG5hcnJvdyBkb3duIGFubm90YXRpb25zLiBGb3IgYXZlcmFnaW5nLCB3ZSB3aWxsIHVzZSBgc2N0cmFuc2Zvcm1gIG5vcm1hbGl6ZWQgZGF0YSBzaW5jZSB0aGlzIGlzIHdoYXQgd2UgdXNlIGZvciBtYXJrZXIgZ2VuZSBkZXRlY3Rpb24gZG93biB0aGUgbGluZS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgYXZlcmFnZSBzY3RyYW5zZm9ybWVkIGV4cHJlc3Npb24Kc2V1ci5hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cikKCiMgYXZlcmFnZWQgZGF0YSB0byB1c2UgZm9yIGNvcnJlbGF0aW9ucyAoU0NUIHRyYW5zZm9ybWVkKQpwbGFjIDwtIHNldXIuYXZnJFNDVApgYGAKCiMjIFJlZmVyZW5jZSBkYXRhCmBgYHtyfQojIHJlYWQgcmVmZXJlbmNlIGRhdGFzZXRzCnJlZiA8LSByZWFkLmNzdigiLi4vcmVzdWx0cy8wMV9yZWZlcmVuY2UtYXRsYXMvdmVudG8tc3VyeWEtcGF2bGlfam9pbmVkLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKYGBgCgojIyBHZXQgZGF0YSBpbiBzaGFwZQpXZSB3aWxsIGpvaW4gdGhlIGRhdGEgd2l0aCB0aGUgcmVmZXJlbmNlIGRhdGEgc28gdGhleSBhcmUgYm90aCBpbiB0aGUgc2FtZSBkYXRhZnJhbWUuIAoKYGBge3J9CiMgYWRkIGdlbmUgbmFtZXMgY29sdW1uCnBsYWMkZ2VuZV9uYW1lIDwtIHJvd25hbWVzKHBsYWMpCgojIGlubmVyIGpvaW4KcGxhYyA8LSBkcGx5cjo6aW5uZXJfam9pbihwbGFjLCByZWYsIGJ5ID0gImdlbmVfbmFtZSIpCgpoZWFkKHBsYWMpCmBgYAoKIyBDb3JyZWxhdGlvbnMgd2l0aGluIHRoZSBkYXRhCkJlZm9yZSBtb3ZpbmcgdG8gYW5ub3RhdGluZyBjbHVzdGVycyBieSBjb21wYXJpc29uIHdpdGggdGhlIHJlZmVyZW5jZSBkYXRhc2V0LCBmaXJzdCB3ZSB3aWxsIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSB3aXRoaW4gdGhlIGRhdGFzZXQuCgojIyBDb3JyZWxhdGlvbiBtYXRyaXgKYGBge3IgZmlnLmFzcD0wLjd9CiMgYnVpbGQgY29ycmVsYXRpb24gbWF0cml4IGZyb20gZXhwcmVzc2lvbiBkYXRhCmNvci5tYXRyaXggPC0gY29yKHBsYWMgJT4lIGRwbHlyOjpzZWxlY3Qoc3RhcnRzX3dpdGgoImNsdXN0IikpLCBtZXRob2QgPSAnc3BlYXJtYW4nKQoKIyByZW9yZGVyIGNvcnJlbGF0aW9uIG1hdHJpeCBiYXNlZCBvbiBjbHVzdGVyaW5nCmRkIDwtIGFzLmRpc3QoKDEgLSBjb3IubWF0cml4KSkgCmhjIDwtIGhjbHVzdChkZCwgbWV0aG9kID0gJ2NvbXBsZXRlJykKY29ybWF0IDwtIGNvci5tYXRyaXhbaGMkb3JkZXIsIGhjJG9yZGVyXQoKIyBtZWx0IGNvcnJlbGF0aW9uIG1hdHJpeApkYXQgPC0gcmVzaGFwZTI6Om1lbHQoY29ybWF0LCBuYS5ybSA9IFQpCgojIyBwbG90CnAgPC0gZ2dwbG90KGRhdGEgPSBkYXQsIGFlcyhWYXIxLCBWYXIyLCBmaWxsID0gdmFsdWUpKSArCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJ3aGl0ZSIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICd3aGl0ZScsIGhpZ2ggPSAncmVkJywKICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiU3BlYXJtYW5cbkNvcnJlbGF0aW9uIikgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkgKwogIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb25zIGFtb25nIGNsdXN0ZXJzIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0xLCB2anVzdCA9IDAuNSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4xNSwgdW5pdHMgPSBjKCdsaW5lcycpKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC44LCB1bml0cyA9IGMoJ2xpbmVzJykpKQoKZ2dzYXZlKHAsIGZpbGVuYW1lID0gIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9wbG90cy9jb3JycGxvdF9jbHVzdGVycy5wZGYiLCAKICAgICAgIGRldmljZSA9ICJwZGYiLCB3aWR0aCA9IDcsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKCnByaW50KHApCmBgYAoKIyMgSGVpcmFyY2hpY2FsIGNsdXN0ZXJpbmcKYGBge3J9CiMgYnVpbGQgY29ycmVsYXRpb24gbWF0cml4IGZyb20gZXhwcmVzc2lvbiBkYXRhCmNvci5tYXRyaXggPC0gY29yKHBsYWMgJT4lIGRwbHlyOjpzZWxlY3Qoc3RhcnRzX3dpdGgoImNsdXN0IikpLCBtZXRob2QgPSAnc3BlYXJtYW4nKQoKIyByZW9yZGVyIGNvcnJlbGF0aW9uIG1hdHJpeCBiYXNlZCBvbiBjbHVzdGVyaW5nCmRkIDwtIGFzLmRpc3QoKDEgLSBjb3IubWF0cml4KSkgCmhjIDwtIGhjbHVzdChkZCwgbWV0aG9kID0gJ2NvbXBsZXRlJykKCnBkZihmaWxlID0gIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9wbG90cy9oY2x1c3RfY2x1c3RlcnMucGRmIiwgd2lkdGggPSA3LCBoZWlnaHQgPSA1KQpwbG90KGhjLCBjZXggPSAwLjgpCmRldi5vZmYoKQpwbG90KGhjLCBjZXggPSAwLjgpCmBgYAoKVGhlcmUgYXJlIHRocmVlIGJpZyBjbHVzdGVyIGJsb2Nrcywgd2l0aCBzdWJzdHJ1Y3R1cmUgd2l0aGluIHRoZW0uIFRoZSBjbHVzdGVyIGJsb2NrcyBhbHNvIGNvcnJlc3BvbmQgd2VsbCB3aXRoIHRoZSBVTUFQIHBsb3QgdGhhdCB3YXMgc2VudCBieSBBbGljZS4gCgojIEFubm90YXRpb24gYnkgY29ycmVsYXRpb24KTGV0J3Mgbm93IGFjdHVhbGx5IG1lYXN1cmUgY29ycmVsYXRpb25zIGJldHdlZW4gYWxsIGNsdXN0ZXJzIHdpdGggdGhlIHJlZmVyZW5jZSBkYXRhc2V0cy4gV2Ugd2lsbCB0cmVhdCBvdXIgZGF0YSBhcyBxdWVyeSBkYXRhc2V0IGFuZCBmb3IgZWFjaCBjbHVzdGVyIGFzc2lnbiB0b3AgMyBjZWxsIHR5cGVzIGluIGVhY2ggcmVmZXJlbmNlIGRhdGFzZXQuCgpgYGB7cn0KcmVmZGF0YSA8LSBjKCJ2ZW50byIsICJzdXJ5YSIsICJwYXZsaSIpCgojIGJ1aWxkIGNvcnJlbGF0aW9uIG1hdHJpeCBmcm9tIGV4cHJlc3Npb24gZGF0YQpjb3IubWF0cml4IDwtIGNvcihwbGFjWywgbmFtZXMocGxhYylbbmFtZXMocGxhYykgIT0gImdlbmVfbmFtZSJdXSwgCiAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICdzcGVhcm1hbicpCgojIHJlb3JkZXIgY29ycmVsYXRpb24gbWF0cml4IGJhc2VkIG9uIGNsdXN0ZXJpbmcKZGQgPC0gYXMuZGlzdCgoMSAtIGNvci5tYXRyaXgpKQpoYyA8LSBoY2x1c3QoZGQsIG1ldGhvZCA9ICdjb21wbGV0ZScpCmNvci5tYXRyaXggPC0gY29yLm1hdHJpeFtoYyRvcmRlciwgaGMkb3JkZXJdCgojIG1lbHQgY29ycmVsYXRpb24gbWF0cml4CmNvcm1hdCA8LSByZXNoYXBlMjo6bWVsdChjb3IubWF0cml4LCBuYS5ybSA9IFQpCmNvcm1hdCRWYXIxX3NvdXJjZSA8LSBzYXBwbHkoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKGNvcm1hdCRWYXIxKSwgc3BsaXQgPSAiXyIpLCAiW1siLCAxKQpjb3JtYXQkVmFyMl9zb3VyY2UgPC0gc2FwcGx5KHN0cnNwbGl0KGFzLmNoYXJhY3Rlcihjb3JtYXQkVmFyMiksIHNwbGl0ID0gIl8iKSwgIltbIiwgMSkKCiMgc3Vic2V0CmNvcm1hdCA8LSBjb3JtYXRbd2hpY2goY29ybWF0JFZhcjFfc291cmNlICVpbiUgcmVmZGF0YSAmIAogICAgICAgICAgICAgICAgICAgY29ybWF0JFZhcjJfc291cmNlID09ICJjbHVzdCIpLCBdCgojIHRvcCAzIG1hdGNoZXMgd2l0aCBoaWdoZXN0IGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cwp0b3BtYXRjaCA8LSBkYXRhLmZyYW1lKCAjIGVtcHR5IGRmIHRvIGZpbGwgdG9wIG1hdGNoZXMgZnJvbSB0aGUgbG9vcCBiZWxvdwogIGNsdXN0ZXIgPSB1bmlxdWUoYXMuY2hhcmFjdGVyKGNvcm1hdCRWYXIyKSkgJT4lIHNvcnQoKSwKICB2ZW50by50b3AxID0gTkEsCiAgc3VyeWEudG9wMSA9IE5BLAogIHBhdmxpLnRvcDEgPSBOQSwKICB2ZW50by50b3AyID0gTkEsCiAgc3VyeWEudG9wMiA9IE5BLAogIHBhdmxpLnRvcDIgPSBOQSwKICB2ZW50by50b3AzID0gTkEsCiAgc3VyeWEudG9wMyA9IE5BLAogIHBhdmxpLnRvcDMgPSBOQQopCgpjb3JtYXQkdG9wMyA8LSBOQQoKZm9yKGkgaW4gdW5pcXVlKGNvcm1hdCRWYXIyKSl7CiAgZm9yKGogaW4gcmVmZGF0YSl7CiAgICAjIGlkZW50aWZ5IHRvcDMgbWF0Y2ggaW5kaWNlcwogICAgaW5kIDwtIHdoaWNoKGNvcm1hdCRWYXIyID09IGkgJiBjb3JtYXQkVmFyMV9zb3VyY2UgPT0gaikKICAgIHZhbCA8LSBjb3JtYXQkdmFsdWVbaW5kXQogICAgdG9wM2luZCA8LSBpbmRbb3JkZXIodmFsLCBkZWNyZWFzaW5nID0gVFJVRSlbMTozXV0KICAgIAogICAgIyBhc3NpZ24gbWF0Y2ggcmFua2luZyB0byBjb3JyZWxhdGlvbiBkYXRhIGZvciBwbG90dGluZwogICAgY29ybWF0JHRvcDNbdG9wM2luZFsxXV0gPC0gIjEiCiAgICBjb3JtYXQkdG9wM1t0b3AzaW5kWzJdXSA8LSAiMiIKICAgIGNvcm1hdCR0b3AzW3RvcDNpbmRbM11dIDwtICIzIgogICAgCiAgICAjIGFzc2lnbiB0b3AgbWF0Y2hlcyB0byB0b3BtYXRjaGVzIGRhdGFmcmFtZQogICAgdG9wbWF0Y2hbdG9wbWF0Y2gkY2x1c3RlciA9PSBpLCBwYXN0ZTAoaiwgIi50b3AxIildIDwtIGdzdWIocGFzdGUwKGosICJfIiksICIiLCBhcy5jaGFyYWN0ZXIoY29ybWF0JFZhcjFbdG9wM2luZFsxXV0pKQogICAgdG9wbWF0Y2hbdG9wbWF0Y2gkY2x1c3RlciA9PSBpLCBwYXN0ZTAoaiwgIi50b3AyIildIDwtIGdzdWIocGFzdGUwKGosICJfIiksICIiLCBhcy5jaGFyYWN0ZXIoY29ybWF0JFZhcjFbdG9wM2luZFsyXV0pKQogICAgdG9wbWF0Y2hbdG9wbWF0Y2gkY2x1c3RlciA9PSBpLCBwYXN0ZTAoaiwgIi50b3AzIildIDwtIGdzdWIocGFzdGUwKGosICJfIiksICIiLCBhcy5jaGFyYWN0ZXIoY29ybWF0JFZhcjFbdG9wM2luZFszXV0pKQogIH0KfQoKYGBgCgojIyBQbG90cwoKYGBge3J9CiMgcGxvdHRpbmcgZnVuY3Rpb24KY2x1c3RBbm5vUGxvdCA8LSBmdW5jdGlvbihkYXQsIHF1ZXJ5LCByZWZlcmVuY2UpewogIAogIGRhdCA8LSBkYXRbd2hpY2goZGF0JFZhcjJfc291cmNlID09IHF1ZXJ5ICYgZGF0JFZhcjFfc291cmNlID09IHJlZmVyZW5jZSksIF0KICAKICBwIDwtIGdncGxvdChkYXRhID0gZGF0LCAKICAgICAgICAgICAgICBhZXMoVmFyMSwgVmFyMiwgZmlsbCA9IHZhbHVlKSkgKwogICAgZ2VvbV90aWxlKGNvbG91ciA9ICJ3aGl0ZSIpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gJ3doaXRlJywgaGlnaCA9ICdyZWQnLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlNwZWFybWFuXG5Db3JyZWxhdGlvbiIpICsKICAgIGdlb21fcG9pbnQoYWVzKFZhcjEsIFZhcjIsIGFscGhhID0gdG9wMyksCiAgICAgICAgICAgICAgIHNpemUgPSAxLjUsIHNoYXBlID0gMTksIHN0cm9rZSAgPSAwKSArCiAgICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygxLCAwLjUsIDAuMjUpLCAKICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDEsIDIsIDMpLAogICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAidG9wMyIsIG5hLnZhbHVlID0gMCkgKwogICAgY29vcmRfZml4ZWQocmF0aW8gPSAxKSArCiAgICB4bGFiKCJyZWZlcmVuY2UiKSArCiAgICB5bGFiKCJxdWVyeSIpICsKICAgIGxhYnMoY2FwdGlvbiA9ICJGb3IgZWFjaCBjZWxsdHlwZSBpbiBxdWVyeSwgYmxhY2sgcG9pbnRzIHJlcHJlc2VudCB0b3AgMyBjZWxsdHlwZXMgZnJvbSByZWZlcmVuY2Ugd2l0aCBoaWdoZXN0IGNvcnJlbGF0aW9uLiIsCiAgICAgICAgIHRpdGxlID0gcGFzdGUwKHF1ZXJ5LCAiIHZzLiAiLCByZWZlcmVuY2UpKSArCiAgICB0aGVtZV9idygpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0PTEsIHZqdXN0ID0gMC41KSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4xNSwgdW5pdHMgPSBjKCdsaW5lcycpKSwKICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpLAogICAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC44LCB1bml0cyA9IGMoJ2xpbmVzJykpKQogIAogIHJldHVybihwKQp9CgpgYGAKCiMjIyBBZ2FpbnN0IFZlbnRvLVRvcm1vIGV0IGFsCmBgYHtyIGZpZy5hc3A9MC45fQojIyBBbm5vdGF0aW9uIHBsb3RzCmFubm8udmVudG8gPC0gY2x1c3RBbm5vUGxvdChkYXQgPSBjb3JtYXQsIHF1ZXJ5ID0gImNsdXN0IiwgcmVmZXJlbmNlID0gInZlbnRvIikKY293cGxvdDo6Z2dzYXZlMihhbm5vLnZlbnRvLCBkZXZpY2UgPSAicGRmIiwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDYuNSwgdW5pdHMgPSAiaW4iLAogICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9wbG90cy9hbm5vdGF0aW9uLWJ5LWNvcnJlbGF0aW9uX3JlZi12ZW50by5wZGYiKQpwcmludChhbm5vLnZlbnRvKQpgYGAKCiMjIyBBZ2FpbnN0IFN1cnlhdmFuc2hpIGV0IGFsCmBgYHtyIGZpZy5hc3A9MC45fQojIyBBbm5vdGF0aW9uIHBsb3RzCmFubm8uc3VyeWEgPC0gY2x1c3RBbm5vUGxvdChkYXQgPSBjb3JtYXQsIHF1ZXJ5ID0gImNsdXN0IiwgcmVmZXJlbmNlID0gInN1cnlhIikKY293cGxvdDo6Z2dzYXZlMihhbm5vLnN1cnlhLCBkZXZpY2UgPSAicGRmIiwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDYuNSwgdW5pdHMgPSAiaW4iLAogICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9wbG90cy9hbm5vdGF0aW9uLWJ5LWNvcnJlbGF0aW9uX3JlZi1zdXJ5YS5wZGYiKQpwcmludChhbm5vLnN1cnlhKQpgYGAKCiMjIyBBZ2FpbnN0IFBhdmxpY2V2IGV0IGFsCmBgYHtyIGZpZy5hc3A9MC45fQojIyBBbm5vdGF0aW9uIHBsb3RzCmFubm8ucGF2bGkgPC0gY2x1c3RBbm5vUGxvdChkYXQgPSBjb3JtYXQsIHF1ZXJ5ID0gImNsdXN0IiwgcmVmZXJlbmNlID0gInBhdmxpIikKY293cGxvdDo6Z2dzYXZlMihhbm5vLnBhdmxpLCBkZXZpY2UgPSAicGRmIiwgd2lkdGggPSA3LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIsCiAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSAiLi4vcmVzdWx0cy8wMl9hbm5vdGF0aW9uL3Bsb3RzL2Fubm90YXRpb24tYnktY29ycmVsYXRpb25fcmVmLXBhdmxpLnBkZiIpCnByaW50KGFubm8ucGF2bGkpCmBgYAoKIyMgVG9wIG1hdGNoZXMKYGBge3J9CnRvcG1hdGNoICU+JSBrYWJsZShjYXB0aW9uID0gIlRvcCAzIG1hdGNoZXMgYWdhaW5zdCBhbGwgcmVmZXJlbmNlIGRhdGFzZXRzLiIpICU+JSBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKCmBgYAoKIyMgTGFiZWxzIGZvciBjbHVzdGVycwpXZSBuZWVkIHRvIGNyZWF0ZSBjb25zaXN0ZW50IGxhYmVscyBmb3IgYWxsIGNlbGwgdHlwZXMgdGhhdCB3ZSBpZGVudGlmeS4gQmVsb3cgaXMgYSBgbGlzdGAgSSBjcmVhdGVkIGluIHdoaWNoIHRoZSB0b3AgbGV2ZWwgb2JqZWN0cyBhcmUgbGFiZWxzIHdlIHdpbGwgZ2l2ZSB0byB0aGUgY2VsbCB0eXBlcywgd2l0aGluIHdoaWNoIGNvbnRhaW5lZCBhcmUgbGlzdHMgb2YgY29ycmVzcG9uZGluZyBsYWJlbHMgZnJvbSBgdmVudG9gIGFuZCBgc3VyeWFgLiBUaGVzZSBhcmUgdGVudGF0aXZlIGxhYmVscy4gQWZ0ZXIgdGhlIGZpcnN0IHBhc3MsIHdoZW4gd2UgZ28gdGhyb3VnaCB0aGUgY2x1c3RlcnMgd2l0aCBhIGZpbmUtdG9vdGhlZCBjb21iLCB3ZSBjYW4gbW9kaWZ5IHRoZW0gZnVydGhlci4gRm9yIGV4YW1wbGUsIGlmIHdlIGhhdmUgbXVsdGlwbGUgY2x1c3RlcnMgbGFiZWxlZCBhcyBgZGVjLk1hY2AgKGRlY2lkdWFsIG1hY3JvcGhhZ2VzKSwgYW5kIGlmIHRob3NlIGNsdXN0ZXJzIGFyZSBkaXN0aW5jdCwgd2UgY2FuIHRoZW4gYnJlYWsgdXAgdGhpcyBsYWJlbCBpbnRvIGBkZWMuTWFjMWAgYW5kIGBkZWMuTWFjMmAuIAoKYGBge3J9CmxhYnMgPC0gbGlzdCgiZGVjLkRTQyIgICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiZFMxIiwgImRTMiIsICJkUzMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJkZWMuRFNDIiwgImRlYy5GQjEiLCAiZGVjLkZCMiIpKSwKICAgICAgICAgICAgICJkZWMuREMiICAgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJEQzEiLCAiREMyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygiZGVjLkRDMSIsICJkZWMuREMyIikpLAogICAgICAgICAgICAgImRlYy5NYWMiICAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoImRNMSIsICJkTTIiLCAiZE0zIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygiZGVjLk1BQyIpKSwKICAgICAgICAgICAgICJkZWMuTksiICAgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJkTksucCIsICJkTksxIiwgImROSzIiLCAiZE5LMyIsICJOSy5DRDE2bmVnIiwgIk5LLkNEMTZwb3MiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJkZWMuTksxIiwgImRlYy5OSzIiKSksCiAgICAgICAgICAgICAiZGVjLlNNQyIgICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiZFAxIiwgImRQMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoImRlYy5TTUMiKSksCiAgICAgICAgICAgICAiZGVjLkVuZG8iICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiRW5kby5tIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygiZGVjLlZFQyIpKSwKICAgICAgICAgICAgICJkZWMuRXBpIiAgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJFcGkxIiwgIkVwaTIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJkZWMuRUVDIikpLAogICAgICAgICAgICAgImRlYy5UY2VsbCIgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIlRjZWxscyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoImRlYy5UQyIpKSwKICAgICAgICAgICAgICJ2aWwuRW5kbyIgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJFbmRvLmYiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJ2aWwuVkVDIikpLAogICAgICAgICAgICAgInZpbC5FVlQiICAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIkVWVCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoInZpbC5FVlQiKSksCiAgICAgICAgICAgICAidmlsLkZCIiAgICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiZkZCMSIsICJmRkIyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygidmlsLkZCMSIsICJ2aWwuRkIyIiwgInZpbC5GQjMiKSksCiAgICAgICAgICAgICAidmlsLkhvZmIiICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiSEIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJ2aWwuSEMiKSksCiAgICAgICAgICAgICAidmlsLlNDVCIgICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiU0NUIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygidmlsLlNDVCIpKSwKICAgICAgICAgICAgICJ2aWwuVkNUIiAgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJWQ1QiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJ2aWwuVkNUIikpLAogICAgICAgICAgICAgInVuay5HcmFuIiAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIkdyYW51bG9jeXRlcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoKSksCiAgICAgICAgICAgICAidW5rLklMQyIgICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiSUxDMyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoKSksCiAgICAgICAgICAgICAidW5rLk1vbm8iICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiTU8iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCkpLAogICAgICAgICAgICAgInVuay5QbGFzbWEiID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIlBsYXNtYSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoKSksCiAgICAgICAgICAgICAidW5rLkVCIiAgICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoInZpbC5FQiIpKSwKICAgICAgICAgICAgICJ1bmsuRW5kby5MIiA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJFbmRvLkwiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJkZWMuTEVDIikpCikKCmBgYAoKIyMgTWF0Y2hlcyBjb25zaXN0ZW50IGJldHdlZW4gcmVmZXJlbmNlIGRhdGFzZXRzClRoZSBmb2xsb3dpbmcgY2x1c3RlcnMgaGF2ZSBjb25zaXN0ZW50IHRvcDEgbWF0Y2ggYmV0d2VlbiBgdmVudG9gIGFuZCBgc3VyeWFgIHJlZmVyZW5jZXMsIGkuZS4gdGhleSBjb3JyZXNwb25kIHRvIHRoZSBzYW1lIGNlbGwgdHlwZSBjYXRlZ29yeS4gVGhlIGBwYXZsaWAgcmVmZXJlbmNlIGlzIHRvbyB1bnJlc29sdmVkIHRvIGJlIHVzZWQgZGlhZ25vc3RpY2FsbHksIHNvIHdlIHdpbGwgaWdub3JlIGl0IGZvciBub3cuIEZvciBzb21lIGNlbGwgdHlwZXMgaXQgaXMgY29uZmlybWF0b3J5LCB0aG91Z2gsIGUuZy4gY2x1c3Rlcl8wMCBjb3JyZXNwb25kcyB0byBEU0MgaW4gYWxsIHRocmVlIHJlZmVyZW5jZXMuIAoKYGBge3J9CiMgZmluZCBvdXQgd2hpY2ggY2x1c3RlcnMgYXJlIGNvbnNpc3RlbnQuCiMgSWYgdmVudG8udG9wMSBhbmQgc3VyeWEudG9wMSBhcmUgZm91bmQgaW4gdGhlIHNhbWUgaXRlbSBpbiB0aGUgbGFicyBsaXN0LCB0aGUgbWFwcGluZyBpcyBjb25zaXN0ZW50Lgpjb25zaXN0ZW50IDwtIGMoTkEpCmZvcihpIGluIDE6bnJvdyh0b3BtYXRjaCkpewogIGNvbnNpc3RlbnRbaV0gPC0gZ3JlcCh0b3BtYXRjaCR2ZW50by50b3AxW2ldLCBsYWJzKSA9PSBncmVwKHRvcG1hdGNoJHN1cnlhLnRvcDFbaV0sIGxhYnMpCn0KCiMgc3Vic2V0IHRvIGluY2x1ZGUgY29uc2lzdGVudCBzZXQKdG9wbWF0Y2guY29ucyA8LSB0b3BtYXRjaCAlPiUgCiAgZmlsdGVyKGNvbnNpc3RlbnQpICU+JSAKICBkcGx5cjo6c2VsZWN0KGNsdXN0ZXIsIHZlbnRvLnRvcDEsIHN1cnlhLnRvcDEpCgojIGFkZCBvdXIgdGVudGF0aXZlIGxhYmVscyB0byB0aGUgY2x1c3RlcnMKZm9yKGkgaW4gMTpucm93KHRvcG1hdGNoLmNvbnMpKXsKICB0b3BtYXRjaC5jb25zJGxhYmVsW2ldIDwtIG5hbWVzKGxhYnMpW2dyZXAodG9wbWF0Y2guY29ucyR2ZW50by50b3AxW2ldLCBsYWJzKV0KfQoKIyBwcmludAp0b3BtYXRjaC5jb25zW29yZGVyKHRvcG1hdGNoLmNvbnMkbGFiZWwpLCBdICU+JSBrbml0cjo6a2FibGUoKSAlPiUga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKIyMgQW1iaWd1b3VzIGNsdXN0ZXJzClRoZSB0b3AxIG1hdGNoZXMgZm9yIHNvbWUgY2x1c3RlcnMgd2l0aCBgdmVudG9gIGFuZCBgc3VyeWFgIGFyZSBub3QgdGhlIHNhbWUuIFRoZSBhcmUgdGhlIGNsdXN0ZXJzIHRoYXQgd2lsbCByZXF1aXJlIGEgbW9yZSBkZXRhaWxlZCBsb29rIHRvIGRldGVybWluZSB0aGVpciBpZGVudGlmeS4gCgpgYGB7cn0KIyBzdWJzZXQgZm9yIGFtYmlndW91cyBjbHVzdGVycwp0b3BtYXRjaC5hbWJpZyA8LSB0b3BtYXRjaCAlPiUgCiAgZmlsdGVyKCFjb25zaXN0ZW50KSAlPiUgCiAgZHBseXI6OnNlbGVjdChjbHVzdGVyLCB2ZW50by50b3AxLCBzdXJ5YS50b3AxKQoKIyBhZGQgb3VyIGxhYmVscy4KZm9yKGkgaW4gMTpucm93KHRvcG1hdGNoLmFtYmlnKSl7CiAgdG9wbWF0Y2guYW1iaWckbGFiZWxbaV0gPC0gcGFzdGUwKAogICAgbmFtZXMobGFicylbZ3JlcCh0b3BtYXRjaC5hbWJpZyR2ZW50by50b3AxW2ldLCBsYWJzKV0sCiAgICAiIG9yICIsCiAgICBuYW1lcyhsYWJzKVtncmVwKHRvcG1hdGNoLmFtYmlnJHN1cnlhLnRvcDFbaV0sIGxhYnMpXQogICAgKQp9CgojIHByaW50CnRvcG1hdGNoLmFtYmlnW29yZGVyKHRvcG1hdGNoLmFtYmlnJGxhYmVsKSwgXSAlPiUga2FibGUoKSAlPiUga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKIyBBbm5vdGF0aW9uIHJlZmluZW1lbnQKVGhlIGFubm90YXRpb25zIHdlIGhhdmUgc28gZmFyIGFyZSBvbmx5IGEgc3RhcnRpbmcgcG9pbnQuIE5vdyB3ZSBjYW4gbG9vayBhdCBlYWNoIGFubm90YXRpb24gbW9yZSBjYXJlZnVsbHkgdG8gbWFrZSBzdXJlIHRoYXQgZXZlcnl0aGluZyBtYWtlcyBzZW5zZS4KCiMjIFNhbXBsZSBjb250cmlidXRpb24gdG8gY2x1c3RlcnMKT25lIG9mIHRoZSB3YXlzIGluIHdoaWNoIHdlIGNhbiByZWZpbmUgdGhlIGNsdXN0ZXJzIGZ1cnRoZXIgaXMgYnkga25vd2luZyB3aGljaCB0aXNzdWUgdGhhdCBzYW1wbGUgYXJpc2VzIGZyb20uIEZvciBleGFtcGxlLCBpZiBhIGNsdXN0ZXIgaGFzIG1hY3JvcGhhZ2UgZ2VuZSBzaWduYXR1cmUgYW5kIGlmIG1vc3QgY2VsbHMgaW4gdGhhdCBjbHVzdGVyIGNvbWUgZnJvbSB2aWxsaSBzYW1wbGVzLCB3ZSBjYW4gZnVydGhlciBuYXJyb3cgZG93biB0aGUgaWRlbnRpdHkgb2YgdGhlIGNsdXN0ZXIgdG8gSG9mYmF1ZXIgY2VsbHMuIFdlIGNhbiBkbyB0aGlzIGJ5IHNpbXBseSBjb3VudGluZyBmb3IgZWFjaCBjbHVzdGVyIGhvdyBtYW55IGNlbGxzIGNvbWUgZnJvbSBkZWNpZHVhIHZzIHZpbGxpIGFuZCBieSBjYWxjdWxhdGluZyB0aGUgcGVyY2VudGFnZS4gCgpgYGB7ciBmaWcuYXNwPTEuMiwgZmlnLndpZHRoPTYuNX0KIyBjb3VudCBjZWxscyBpbiBlYWNoIGNsdXN0ZXIgYnkgdGlzc3VlIG9mIG9yaWdpbgpieXRpc3N1ZSA8LSB0YWJsZShzZXVyJHRpc3N1ZSwgc2V1ciRzZXVyYXRfY2x1c3RlcnMpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogIGRwbHlyOjpyZW5hbWUodGlzc3VlID0gVmFyMSwgY2x1c3RlciA9IFZhcjIsIGZyZXF1ZW5jeSA9IEZyZXEpCgojIGNhbGN1bGF0ZSBmcmFjdGlvbgpmb3IoaSBpbiAxOm5yb3coYnl0aXNzdWUpKXsKICBieXRpc3N1ZSRmcmFjdGlvbltpXSA8LSBieXRpc3N1ZSRmcmVxdWVuY3lbaV0vCiAgICBzdW0oYnl0aXNzdWUkZnJlcXVlbmN5W2J5dGlzc3VlJGNsdXN0ZXIgPT0gYnl0aXNzdWUkY2x1c3RlcltpXV0pCn0KCiMgcGxvdApwLmNlbGxzIDwtIGdncGxvdChkYXRhID0gYnl0aXNzdWUsIGFlcyh4ID0gZnJlcXVlbmN5LCB5ID0gY2x1c3RlcikpICsKICBnZW9tX2JhcihhZXMoZmlsbCA9IHRpc3N1ZSksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsKICBnZ3RpdGxlKCJOdW1iZXIgb2YgY2VsbHMiKQoKcC5mcmFjIDwtIGdncGxvdChkYXRhID0gYnl0aXNzdWUsIGFlcyh4ID0gZnJhY3Rpb24sIHkgPSBjbHVzdGVyKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsID0gdGlzc3VlKSwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gInN0YWNrIikgKwogIGdndGl0bGUoIkZyYWN0aW9uIG9mIGNlbGxzIikKCnAuY2VsbHMgKyBwLmZyYWMgKyBwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIsIG5yb3cgPSAyKSArCiAgcGxvdF9hbm5vdGF0aW9uKGNhcHRpb24gPSAiU2FtcGxlIGNvbnRyaWJ1dGlvbiB0byBjbHVzdGVycyIpICYKICBjb29yZF9mbGlwKCkgJgogIHNjYWxlX2ZpbGxfdGFibGVhdShwYWxldHRlID0gIkNsYXNzaWMgMTAgTWVkaXVtIikgJgogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjI1KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUpKQogIAoKYGBgCkZvbGxvd2luZyBjbHVzdGVycyBoYXZlIG1vcmUgdGhhbiA3NSUgY2VsbHMgY29taW5nIGZyb20gdGhlIHNhbWUgdGlzc3VlIG9mIG9yaWdpbi4gCgpgYGB7cn0KYnl0aXNzdWVbLCBjKCJjbHVzdGVyIiwgInRpc3N1ZSIsICJmcmFjdGlvbiIpXSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJ0aXNzdWUiLCB2YWx1ZXNfZnJvbSA9ICJmcmFjdGlvbiIpICU+JSAKICBmaWx0ZXIoZGVjaWR1YSA+IDAuNzUgfCB2aWxsaSA+IDAuNzUpICU+JSAKICBrYWJsZShkaWdpdHMgPSAyLCByb3cubmFtZXMgPSBGQUxTRSwgCiAgICAgICAgY2FwdGlvbiA9ICJDbHVzdGVycyB3aXRoIG1vcmUgdGhhbiA3NSUgY2VsbHMgZnJvbSBvbmUgdGlzc3VlIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKRm9sbG93aW5nIGNsdXN0ZXJzIGhhdmUgYW1iaWd1b3VzIG9yaWdpbiwgaS5lLiB0aGV5IGNvbnRhaW4gY2VsbHMgZnJvbSBkZWNpZHVhIGFuZCB2aWxsaSBzYW1wbGVzLiAKCmBgYHtyfQpieXRpc3N1ZVssIGMoImNsdXN0ZXIiLCAidGlzc3VlIiwgImZyYWN0aW9uIildICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gInRpc3N1ZSIsIHZhbHVlc19mcm9tID0gImZyYWN0aW9uIikgJT4lIAogIGZpbHRlcihkZWNpZHVhIDwgMC43NSAmIHZpbGxpIDwgMC43NSkgJT4lIAogIGthYmxlKGRpZ2l0cyA9IDIsIHJvdy5uYW1lcyA9IEZBTFNFLCAKICAgICAgICBjYXB0aW9uID0gIkNsdXN0ZXJzIHdpdGggY2VsbHMgZnJvbSBib3RoIHRpc3N1ZXMiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgpUaGlzIGRvZXNuJ3QgbmVjZXNzYXJpbHkgY2xhcmlmeSB0b28gbWFueSB0aGluZ3MuIFRoZXJlIGFyZSBtYW55IGNsdXN0ZXJzIHRoYXQgaGF2ZSBvdmVyIDMvNHRoIG9mIHRoZSBjZWxscyBjb21pbmcgZnJvbSB0aGUgc2FtZSB0aXNzdWUsIGJ1dCBvZnRlbiBub3QgdGhlIHRpc3N1ZSB5b3UgZXhwZWN0LiBGb3IgZXhhbXBsZSBjbHVzdGVycyAxNiBhbmQgMTcgYXJlIGVpdGhlciB2aWxsb3VzIG9yIHN5bmN5dGlhbCB0cm9waG9ibGFzdCBjZWxscyBidXQgdGhleSBhcmUgY29taW5nIG1vc3RseSBmcm9tIGRlY2lkdWEgc2FtcGxlcy4gWW91IGV4cGVjdCB0aGF0IGZvciBjbHVzdGVyIDEzLCB3aGljaCBpcyBleHRyYXZpbGxvdXMgdHJvcGhvYmxhc3QgYnV0IG5vdCBmb3IgdGhlIG90aGVyIHR5cGVzLiBBdCBsZWFzdCBpbiB0aGUgc3BlY2llcyB0aGF0IEkgaGF2ZSB3b3JrZWQgd2l0aCwgaXQgaXMgb2Z0ZW4gZGlmZmljdWx0IHRvIHNlcGFyYXRlIHRoZSBmZXRhbCB0aXNzdWUgZnJvbSBtYXRlcm5hbCB0aXNzdWUuIFNvIHRoaXMgbWl4ZWQgcmVzdWx0IGNvdWxkIGJlIGEgcmVzdWx0IG9mIHRoYXQgZGlmZmljdWx0eS4gSXQgbG9va3MgbGlrZSB3ZSBjYW4gdXNlIHRoaXMgYSByb3VnaCBndWlkZSwgYnV0IHdlIGNhbid0IG5lY2Vzc2FyaWx5IHJlbHkgb2YgdGhpcyBhbmFseXNpcywgaS5lLiB0aGUgbWFya2VyIGdlbmUgc2hvdWxkIHRha2UgcHJlY2VkZW5jZSBvdmVyIHRpc3N1ZSBvZiBvcmlnaW4gaW4gY2VsbCB0eXBlIGlkIGJlY2F1c2UgdGhlIGxhdHRlciBpcyBleHBlcmltZW50YWxseSAoYW5kIGFzIGEgY29uc2VxdWVuY2UsIGFuYWx5dGljYWxseSkgbWVzc3kgdG8gZGlzZW50YW5nbGUuICAKCiMjIE1hcmtlciBnZW5lcwojIyMgSWRlbnRpZnkgbWFya2VyIGdlbmVzIApJIHJhbiBgU2V1cmF0OjpmaW5kQWxsTWFya2Vyc2Agb24gdGhpcyBkYXRhIHVzaW5nIHRoZSBgU0NUYCBhc3NheS4gVGhpcyBmdW5jdGlvbiBvdXRwdXRzIGEgbGlzdCBvZiBtYXJrZXIgZ2VuZXMgZm9yIGVhY2ggY2x1c3RlciBjb21wYXJlZCB0byBhbGwgb3RoZXIgY2VsbHMgaW4gdGhlIGRhdGEuIFRoaXMgdGFrZXMgYSB3aGlsZSAoaG91cnMpIHRvIHJ1biwgc28gSSByYW4gaXQgb24gdGhlIGNsdXN0ZXIgYW5kIHNhdmVkIHRoZSByZXN1bHRzIHRvIGEgYC5jc3ZgLiBTZWUgdGhlIGBjb2RlYCBkaXJlY3RvcnkgZm9yIGZ1bGwgY29kZSwgYW5kIHNlZSBiZWxvdyBmb3IgdGhlIGZ1bmN0aW9uIGNhbGwuIAoKYGBge3IgZWNobz1UUlVFLCBldmFsPUZBTFNFfQojIHNldCBkZWZhdWx0IGFzc2F5IHRvIFNDVApEZWZhdWx0QXNzYXkob2JqZWN0ID0gcGxhYykgPC0gIlNDVCIKCiMgZmluZCBtYXJrZXJzIGZvciBhbGwgY2x1c3RlcnMKbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhvYmplY3QgPSBwbGFjLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5LnBvcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMC4yNSkgIAoKIyB3cml0ZS5jc3YKd3JpdGUuY3N2KG1hcmtlcnMsICIvaG9tZS9hcmM3OC9zY3JhdGNoNjAvY292aWQtcGxhY2VudGEvbWFya2Vyc19zY3QuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKYGBge3J9Cm1hcmtlcnMgPC0gcmVhZC5jc3YoIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9maWxlcy9tYXJrZXJzX3NjdC5jc3YiKQoKIyByZW5hbWUgY2x1c3RlcnMKbWFya2VycyRjbHVzdGVyIDwtIHBhc3RlMCgiY2x1c3RfIiwgbWFya2VycyRjbHVzdGVyKQptYXJrZXJzJGNsdXN0ZXIgPC0gZ3N1YigiKGNsdXN0XykoXFxkKSQiLCAiXFwxMFxcMiIsIG1hcmtlcnMkY2x1c3RlcikKbWFya2VycyRjbHVzdGVyIDwtIGZhY3RvcihtYXJrZXJzJGNsdXN0ZXIsIGxldmVscyA9IHVuaXF1ZShtYXJrZXJzJGNsdXN0ZXIpKQoKIyBzcGxpdCBieSBjbHVzdGVyCm1hcmtlcnMgPC0gc3BsaXQuZGF0YS5mcmFtZShtYXJrZXJzLCBtYXJrZXJzJGNsdXN0ZXIpCmBgYAoKIyMjIFBsb3QgbWFya2VyIGdlbmVzCldlIHdpbGwgcGxvdCB0b3AgNTAgbWFya2VyIGdlbmVzIGZvciBhbGwgY2x1c3RlcnMgYW5kIHNhdmUgdGhlIHBsb3QgdG8gcGRmLiAKYGBge3J9CiMgcGxvdHRpbmcgZnVuY3Rpb24KRG90UGxvdDIgPC0gZnVuY3Rpb24ob2JqZWN0ID0gInNldXIiLCBhc3NheSA9ICJTQ1QiLCBmZWF0dXJlcywgdGl0bGUsIC4uLikgewogIHAgPC0gRG90UGxvdChzZXVyLCAKICAgICAgICAgICAgICAgYXNzYXkgPSAiU0NUIiwKICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBmZWF0dXJlcywgCiAgICAgICAgICAgICAgIGRvdC5taW4gPSAwLjA1LCBkb3Quc2NhbGUgPSA0KSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgbGFicyhjYXB0aW9uID0gcGFzdGUwKGFzc2F5LCAiIG5vcm1hbGl6ZWQgZXhwcmVzc2lvbiIpLCB0aXRsZSA9IHRpdGxlKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSksCiAgICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKQogIAogIHJldHVybihwKQp9CmBgYAoKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90cyA8LSBsaXN0KCkKZm9yKGkgaW4gbmFtZXMobWFya2VycykpIHsKICBpZihucm93KG1hcmtlcnNbW2ldXSkgPiA1MCkKICAgIGZlYXR1cmVzIDwtIG1hcmtlcnNbW2ldXSRnZW5lWzE6NTBdCiAgaWYobnJvdyhtYXJrZXJzW1tpXV0pIDwgNTApCiAgICBmZWF0dXJlcyA8LSBtYXJrZXJzW1tpXV0kZ2VuZQoKICB0b3A1MG1hcmtlci5wbG90c1tbaV1dIDwtIERvdFBsb3QyKGZlYXR1cmVzID0gZmVhdHVyZXMsIHRpdGxlID0gaSkKICAgICAgICAgCiAgY293cGxvdDo6Z2dzYXZlMih0b3A1MG1hcmtlci5wbG90c1tbaV1dLCAKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKCIuLi9yZXN1bHRzLzAyX2Fubm90YXRpb24vcGxvdHMvdG9wNTBtYXJrZXJzX2RvdHBsb3RfIiwgaSwgIi5wZGYiKSwKICAgICAgICAgICAgICAgICAgIHdpZHRoID0gNy41LCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKfQpgYGAKCiMgTWFudWFsIGFubm90YXRpb24KV2l0aCB0aGUgbWFya2VyIGdlbmUgcGxvdHMgd2UgaGF2ZSBtYWRlLCB3ZSBjYW4gZXhhbWluZSB0aGUgdG9wIG1hcmtlciBnZW5lcyBpbiBlYWNoIGNsdXN0ZXIgdG8gbWFudWFsbHkgY29uZmlybSBvciBhZGp1c3QgdGhlIGFubm90YXRpb24gb2YgZWFjaCBjbHVzdGVyLgoKTGV0J3MgbWFrZSBhIGRhdGFmcmFtZSBpbiB3aGljaCB0byBrZWVwIHRyYWNrIG9mIHRoZSBhc3NpZ25tZW50cy4KCmBgYHtyfQphbm5vdGF0aW9uIDwtIGRhdGEuZnJhbWUoCiAgY2x1c3RlciA9IHVuaXF1ZShzZXVyJHNldXJhdF9jbHVzdGVycykgJT4lIHNvcnQoKSwKICBhbm5vdGF0aW9uID0gTkEsCiAgbm90ZXMgPSBOQQopCmBgYAoKRm9yIGVhY2ggc2V0IG9mIG1hcmtlciBnZW5lcywgd2UgY2FuIGFsc28gcGVyZm9ybSBhIG51bWJlciBvZiBtYW51YWwgY2hlY2tzIHRvIGFzc2VzcyBjZWxsIHR5cGUgYW5ub3RhdGlvbi4gIAoKRmlyc3QgaXMgR08gZW5yaWNobWVudCB1c2luZyBgY2x1c3RlclByb2ZpbGVgIHBhY2thZ2UsIGZvciB3aGljaCBiZWxvdyBpcyBhIGZ1bmN0aW9uIGZvciBxdWlja2x5IGxvb2tpbmcgYXQgdG9wIDEwIGVucmljaGVkIEdPIGNhdGVnb3JpZXMuCmBgYHtyfQojIGZ1bmN0aW9uIGZvciBHTyBlbnJpY2htZW50CiMgMS4gZm9yIGEgZ2l2ZW4gY2x1c3RlciwgcHJpbnRzIG4ucHJpbnQucm93cyB0b3AgZW5yaWNoZWQgR08gdGVybXMKIyAyLiBDYW4gZXhjbHVkZSByaWJvc29tYWwgZ2VuZXMgKGRlZmF1bHQpIGJlY2F1c2UgdGhleSBkb21pbmF0ZSBzb21lIEdPIGVucmljaG1lbnRzCmVucmljaEdPMiA8LSBmdW5jdGlvbihjbHVzdCwgbmdlbmVzID0gMTAwLCBpbmNsdWRlLnJpYm8gPSBGQUxTRSwgbi5wcmludC5yb3dzID0gMTAsIC4uLikgewogIAogIGlkcy5iaXRyIDwtIGJpdHIobWFya2Vyc1tbY2x1c3RdXSRnZW5lWzE6bmdlbmVzXSwgCiAgICAgICAgICAgICAgICBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSBjKCJFTlRSRVpJRCIsICJTWU1CT0wiKSwgCiAgICAgICAgICAgICAgICBPcmdEYiA9ICJvcmcuSHMuZWcuZGIiKQogICAgCiAgaWYoaW5jbHVkZS5yaWJvID09IEZBTFNFKQogICAgZmVhdHVyZXMgPC0gaWRzLmJpdHIkRU5UUkVaSURbIShncmVwbCgiUlBbU0xdIiwgaWRzLmJpdHIkU1lNQk9MKSldCiAgaWYoaW5jbHVkZS5yaWJvID09IFRSVUUpCiAgICBmZWF0dXJlcyA8LSBpZHMuYml0ciRFTlRSRVpJRAoKICBlZ28gPC0gZW5yaWNoR08oCiAgICBnZW5lICAgICAgICAgID0gZmVhdHVyZXMsICAgIAogICAgT3JnRGIgICAgICAgICA9IG9yZy5Icy5lZy5kYiwKICAgIG9udCAgICAgICAgICAgPSAiQlAiLAogICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICBwdmFsdWVDdXRvZmYgID0gMC4wMSwgIyBkZWZhdWx0IDAuMDEKICAgIHF2YWx1ZUN1dG9mZiAgPSAwLjA1LCAjIGRlZmF1bHQgMC4wNQogICAgcmVhZGFibGUgPSBUUlVFKQogIAogIGRmdG9wcmludCA8LSBjbHVzdGVyUHJvZmlsZXI6OnNpbXBsaWZ5KGVnbylAcmVzdWx0WzE6bi5wcmludC5yb3dzLCBjKDIsOCldCiAgcm93bmFtZXMoZGZ0b3ByaW50KSA8LSBOVUxMCiAgCiAgcmV0dXJuKGRmdG9wcmludCkKfQpgYGAKCldlIGNhbiBhbHNvIGNoZWNrIGlmIHRoZSBjbHVzdGVyIGlzIGVucmljaGVkIGlzIGdlbmVzIHRoYXQgYXJlIGxhYmVsZWQgYXMgbWFya2VycyBnZW5lcyBmb3IgYSBjZWxsIHR5cGUgYnkgb3RoZXIgc3R1ZGllcy4gW0B2ZW50by10b3Jtb19zaW5nbGUtY2VsbF8yMDE4XSBoYXZlIHByb3ZpZGVkIHRvcCAzMCBtYXJrZXIgZ2VuZXMgZm9yIGFsbCBjZWxsIHR5cGVzIGlkZW50aWZpZWQgYnkgdGhlbS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgcmVhZCB0b3AgMzAgbWFya2VycyBmcm9tIHZlbnRvLXRvcm1vIGV0IGFsCm1hcmtlcnMudmVudG8gPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKCIuLi9pbmZvL2Zyb20tcGFwZXJzL3ZlbnRvLXRvcm1vXzIwMTgvNDE1ODZfMjAxOF82OThfTU9FU00xX0VTTS9TdXBwbGVtZW50YXJ5IFRhYmxlIDIueGxzeCIpCgojIHJlbmFtZSBjb2x1bW5zCm1hcmtlcnMudmVudG8gPC0gZHBseXI6OnJlbmFtZShtYXJrZXJzLnZlbnRvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVkNUXzEgPSBWQ1QuLi4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGNlbGxzXzEgPSBgVCBjZWxscy4uLjVgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGNlbGxzXzIgPSBgVCBjZWxscy4uLjlgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVkNUXzIgPSBgVkNULi4uMTBgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGNlbGxzXzMgPSBgVCBjZWxscy4uLjExYCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZCXzEgPSBGMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVWVF8xID0gYEVWVC4uLjE0YCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRjZWxsc180ID0gYFQgY2VsbHMuLi4xNWAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFVlRfMiA9IGBFVlQuLi4xNmAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOS0NEMTZwb3MgPSBgQmxvb2QgTksgQ0QxNitgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTU9fMSA9IGBNTy4uLjIyYCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1PXzIgPSBgTU8uLi4yN2AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOS0NEMTZuZWcgPSBgQmxvb2QgTksgQ0QxNi1gLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRkIyID0gRjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFbmRvLm0gPSBgRW5kbyAobSlgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRW5kby5MID0gYEVuZG8gTGAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFbmRvLmYgPSBgRW5kbyAoZilgIAopCgojIHJlbW92ZSBlbnNlbWJsIGlkcwptYXJrZXJzLnZlbnRvIDwtIGFwcGx5KFggPSBtYXJrZXJzLnZlbnRvLCBNQVJHSU4gPSAyLCBGVU4gPSBmdW5jdGlvbih4KSBnc3ViKCJfRU5TR1xcZCsiLCAiIiwgeCkpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkKYGBgCgpbQHN1cnlhd2Fuc2hpX3NpbmdsZS1jZWxsXzIwMThdIGhhdmUgYWxzbyBwcm92aWRlZCBhIGxpc3Qgb2YgdG9wMzAgbWFya2VyIGdlbmVzIGluIHRoZSBzdXBwbGVtZW50YXJ5IGRhdGEuIApgYGB7cn0KbWFya2Vycy5zdXJ5YS52aWwgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKCIuLi9pbmZvL2Zyb20tcGFwZXJzL3N1cnlhd2Fuc2hpXzIwMTgvc3VwcC1kYXRhL2FhdTQ3ODhfRGF0YV9maWxlX1MzLnhsc3giLCBzaGVldCA9ICJWaWxsaSIsIHJhbmdlID0gIkExOkcyNzEiLCB0cmltX3dzID0gVFJVRSkKbWFya2Vycy5zdXJ5YS5kZWMgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKCIuLi9pbmZvL2Zyb20tcGFwZXJzL3N1cnlhd2Fuc2hpXzIwMTgvc3VwcC1kYXRhL2FhdTQ3ODhfRGF0YV9maWxlX1MzLnhsc3giLCBzaGVldCA9ICJEZWNpdWRhIiwgcmFuZ2UgPSAiQTE6RzMzMSIsIHRyaW1fd3MgPSBUUlVFKQoKbWFya2Vycy5zdXJ5YS52aWwgPC0gZHBseXI6OnJlbmFtZShtYXJrZXJzLnN1cnlhLnZpbCwgY2x1c3RlciA9IGBjZWxsIHR5cGVgKQoKbWFya2Vycy5zdXJ5YS52aWwkY2x1c3RlciA8LSBwYXN0ZTAoInZpbC4iLCBtYXJrZXJzLnN1cnlhLnZpbCRjbHVzdGVyKQptYXJrZXJzLnN1cnlhLmRlYyRgY2x1c3RlcmAgPC0gcGFzdGUwKCJkZWMuIiwgbWFya2Vycy5zdXJ5YS5kZWMkYGNsdXN0ZXJgKQoKbWFya2Vycy5zdXJ5YSA8LSByYmluZChtYXJrZXJzLnN1cnlhLnZpbCwgbWFya2Vycy5zdXJ5YS5kZWMpCmBgYAoKCiMjIENsdXN0ZXJzIDAgYW5kIDE4CiMjIyBOb3RlcwoxLiBDbHVzdGVycyAwIGFuZCAxOCBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGVhY2ggb3RoZXIgYW5kIG1vc3Qgc2ltaWxhciB0byBlYWNoIG90aGVyIHRoYW4gdG8gb3RoZXIgY2x1c3RlcnMuIAoyLiBCb3RoIGFyZSBjb25zaXN0ZW50bHkgc2ltaWxhciB0byB0aGUgZGVjaWR1YWwgc3Ryb21hbCBjZWxscyBpbiBib3RoIHZlbnRvIGFuZCBzdXJ5YSBkYXRhc2V0cy4gCjMuIFRoZSBtYXJrZXIgZ2VuZXMgaW5jbHVkZSBQcm9sYWN0aW4sIElHRkJQMSBldGMsIHdoaWNoIGFyZSB0eXBpY2FsIG9mIGRlY2lkdWFsIHN0cm9tYWwgY2VsbHMuIAo0LiBUaGVzZSB0d28gY2x1c3RlcnMgYWxzbyBhcmlzZSB1bmFiaWd1b3VzbHkgZnJvbSB0aGUgZGVjaWR1YSBzYW1wbGVzIHJhdGhlciB0aGFuIHZpbGxpIHNhbXBsZXMuCgpgYGB7cn0KYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzAwIiwgImNsdXN0XzE4IildIDwtICJkZWMuRFNDIgoKYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBwYXN0ZTAoImNsdXN0XyIsIGMoIjAwIiwgIjE4IikpLCBdCmBgYAoKIyMjIE1hcmtlciBnZW5lcyBwbG90cwpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMDAiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMTgiXV0KYGBgCgojIyMgR28gYW5yaWNobWVudApgYGB7cn0KZW5yaWNoR08yKCJjbHVzdF8wMCIpCmBgYAoKIyMjIFZlbnRvIG1hcmtlcnMgZm9yIERTQwpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJGRTMywgdGl0bGUgPSAidmVudG86IGRTMyIpCmBgYAoKIyMjIFN1cnlhIG1hcmtlcnMgZm9yIERTQwpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJkZWMuRFNDIl0sIHRpdGxlID0gInN1cnlhIG1hcmtlcnM6IERTQyIpCmBgYAoKIyMgQ2x1c3RlcnMgNCBhbmQgMjEKIyMjIE5vdGVzCgoxLiBDbHVzdGVycyA0IGFuZCAyMSBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQuIAoyLiBUaGV5IGNvbnNpc3RlbnRseSBnZXQgYXNzaWduZWQgYXMgZGVjaWR1YWwgZW5kb3RoZWxpYWwgY2VsbHMgYnkgYm90aCB2ZW50byBhbmQgc3VyeWEgZGF0YXNldHMuCjMuIE1vcmUgdGhhbiA3NSUgb2YgdGhlIGNlbGxzIGluIGJvdGggb2YgdGhlc2UgY2x1c3RlcnMgY29tZSBmcm9tIHRoZSBkZWNpZHVhIHNhbXBsZXMuCjQuIE1hcmtlcnMgZ2VuZXMgb2YgYm90aCBjbHVzdGVycyBhcmUgZW5yaWNoZWQgaW4gR08gY2F0ZWdvcmllcyByZWxhdGVkIHRvIGVwaXRoZWxpdW0gZGV2ZWxvcG1lbnQsIGVuZG90aGVsaXVtIGRldmVsb3BtZW50IGV0Yy4gCjUuIEJ1dCB0aGVzZSB0d28gY2x1c3RlcnMgYXJlIG5vdCBhcyBhbGlrZSBhcyBjbHVzdGVycyAwIGFuZCAxOC4gVGhleSBhcmUgZGlmZmVyZW50IGluIHRlcm1zIG9mIHRoZWlyIGV4cHJlc3Npb24gb2YgbWFueSBlbmRvdGhlbGl1bSByZWxhdGVkIGdlbmVzIGxpa2UgQ0QzNCwgUFJPWDEsIFZXRiwgU1BBUkNMLiBDbHVzdGVyIDIxIGxvb2tzIG1vcmUgbGlrZSAidHlwaWNhbCIgZW5kb3RoZWxpYWwgY2VsbHMuIENsdXN0ZXIgNCBoYXMgbG93IGV4cHJlc3Npb24gb2YgbWFueSB0eXBpY2FsIGVuZG90aGVsaWFsIGdlbmVzLiAKNi4gQ2x1c3RlciA0IGhhcyBoaWdoZXIgZXhwcmVzc2lvbiBvZiBzb21lIGx5bXBoYXRpYyBlbmRvdGhlbGlhbCBjZWxsczogUFJPWDEsIExZVkUxLiBNYW55IGdlbmVzIGZyb20gdGhlIGxpc3Qgb2Ygc3VyeWEgbWFya2VyIGdlbmVzIGZvciBkZWMuTEVDIGFyZSBleHByZXNzZWQgaW4gY2x1c3RlciA0LiBUaGlzIHN1Z2dlc3RzIHRoYXQgQ2x1c3RlciA0IG1heSBiZSBseW1waGF0aWMgZW5kb3RoZWxpYWwgY2VsbHMgYW5kIGNsdXN0ZXIgMjEgbWF5IGJlIGVuZG90aGVsaWFsIGNlbGxzLiAKCmBgYHtyfQphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMDQiKV0gPC0gImRlYy5FbmRvLkwiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8yMSIpXSA8LSAiZGVjLkVuZG8iCgphbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIHBhc3RlMCgiY2x1c3RfIiwgYygiMDQiLCAiMjEiKSksIF0KYGBgCgojIyMgTWFya2VyIGdlbmUgcGxvdHMKYGBge3IsIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8wNCJdXQpgYGAKCmBgYHtyLCBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMjEiXV0KYGBgCgojIyMgR08gZW5yaWNobWVudApgYGB7cn0KZW5yaWNoR08yKCJjbHVzdF8wNCIpCmBgYAoKYGBge3J9CmVucmljaEdPMigiY2x1c3RfMjEiKQpgYGAKCiMjIyBWZW50byBtYXJrZXJzIGZvciBlbmRvdGhlbGlhbCBjZWxscwpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJEVuZG8ubSwgdGl0bGUgPSAidmVudG86IEVuZG8ubSIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRFbmRvLmYsIHRpdGxlID0gInZlbnRvOiBFbmRvLmYiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kRW5kby5MLCB0aXRsZSA9ICJ2ZW50bzogRW5kby5MIikKYGBgCgojIyMgU3VyeWEgbWFya2VycyBmb3IgZW5kb3RoZWxpYWwgY2VsbHMKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy5zdXJ5YSRnZW5lW21hcmtlcnMuc3VyeWEkY2x1c3RlciA9PSAidmlsLlZFQyJdLCB0aXRsZSA9ICJzdXJ5YSBtYXJrZXJzOiB2aWwuVkVDIikKYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJkZWMuVkVDIl0sIHRpdGxlID0gInN1cnlhIG1hcmtlcnM6IGRlYy5WRUMiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMuc3VyeWEkZ2VuZVttYXJrZXJzLnN1cnlhJGNsdXN0ZXIgPT0gImRlYy5MRUMiXSwgdGl0bGUgPSAic3VyeWEgbWFya2VyczogZGVjLkxFQyIpCmBgYAoKIyMgQ2x1c3RlciAxMwojIyMgTm90ZXMKCjEuIFRoaXMgY2x1c3RlciBpcyB1bmlxdWUgaW4gaXRzIGV4cHJlc3Npb24gb2YgSExBLUcsIHR5cGljYWwgb2YgZXh0cmF2aWxsb3VzIHRyb3Bob2JsYXN0LiAKMi4gU2V2ZXJhbCBvdGhlciBnZW5lcyBvZnRlbiBlbnJpY2hlZCBpbiBFVlQgYXJlIGFsc28gZW5yaWNoZWQgaW4gdGhpcyBjbHVzdGVyOiBGTjEsIFBBUFBBMiwgRElPMiwgZXRjLgozLiB0b3AgMzAgbWFya2VycyBhc3NvY2lhdGVkIHdpdGggRVZUXzIgZnJvbSB2ZW50byBkYXRhc2V0IGFyZSBhbHNvIGhpZ2hseSBhbmQgc3BlY2lmaWNhbGx5IGV4cHJlc3NlZCBpbiB0aGlzIGNsdXN0ZXIuIAo0LiBJdCBpcyBjb25zaXN0ZW50bHkgYXNzaWduZWQgdG8gYmUgRVZUIGJhc2VkIG9uIHZlbnRvLCBzdXJ5YSwgYW5kIHBhdmxpIGRhdGFzZXRzLiAKCkFubm90YXRpb246ICAKKipDbHVzdGVyIDEzOiBFeHRyYXZpbGxvdXMgdHJvcGhvYmxhc3QgKHZpbC5FVlQpKioKCmBgYHtyfQphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMTMiKV0gPC0gInZpbC5FVlQiCgphbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzEzIiksIF0KYGBgCgojIyMgTWFya2VyIGdlbmVzIHBsb3QKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzEzIl1dCmBgYAoKIyMjIEdPIGVucmljaG1lbnQKYGBge3IgbWVzc2FnZT1GQUxTRX0KZW5yaWNoR08yKCJjbHVzdF8xMyIpCmBgYAoKIyMjIFZlbnRvIG1hcmtlcnMgZm9yIEVWVApgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJEVWVF8xLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBFVlRfMSIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRFVlRfMiwgdGl0bGUgPSAidmVudG8gbWFya2VyczogRVZUXzIiKQpgYGAKCiMjIyBTdXJ5YSBtYXJrZXIgZ2VuZXMgZm9yIEVWVApgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJ2aWwuRVZUIl0sIHRpdGxlID0gInN1cnlhIG1hcmtlcnM6IHZpbC5FVlQiKQpgYGAKCiMjIE90aGVyIHRyb3Bob2JsYXN0IGNsdXN0ZXJzICgxNiwgMTcsIDIzLCAyNCwgMjUsIDI5KQojIyMgTm90ZXMKRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gZXhwcmVzc2lvbiBwYXR0ZXJucyBhbmQgbWFya2VycyBvZiBkaWZmZXJlbnQgdHJvcGhvYmxhc3QgdHlwZXMsIHNlZSBbQGxpdV9zaW5nbGUtY2VsbF8yMDE4XS4KCjEuIEluIGFkZGl0aW9uIHRvIGNsdXN0ZXIgMTMgKEVWVCksIHRoZXJlIGFyZSA2IG90aGVyIGNsdXN0ZXJzIHRoYXQgYXJlIGxpa2VseSB0cm9waG9ibGFzdCBjZWxsIHR5cGVzICh0aGV5IGV4cHJlc3MgS1JUNyBhbmQgYXJlIHJlbGF0ZWQgY2x1c3RlcnMpLiBUaGVzZSA2IGNsdXN0ZXJzIGFyZSBvcmdhbml6ZWQgaW50byB0d28gY29ycmVsYXRlZCBibG9ja3MgKHNlZSAiY29ycmVsYXRpb25zIHNlY3Rpb24gYWJvdmUiKTogKDE2LCAxNywgMjUpIGFuZCAoMjMsIDI5LCAyNCkgb2Ygd2hpY2ggdGhlIGZvcm1lciBhcmUgbGlrZWx5IFZDVCBhbmQgdGhlIGxhdHRlciBTQ1QgYWNjb3JkaW5nIHRvIGNvcnJlbGF0aW9uLWJhc2VkIGFubm90YXRpb24uIAoyLiBDbHVzdGVyIDI5IGFwcGVhcnMgdG8gYmUgU0NUIGdpdmVuIGl0cyBleHByZXNzaW9uIG9mIENHQSwgR0RGMTUsIEVSVkZSRC0xIChTeW5jeXRpbiAyKSBnZW5lcy4gRm9yIHNvbWUgcmVhc29uIEdPIGVucmljaG1lbnQgZGlkbid0IHdvcmsgZm9yIG1hcmtlcnMgb2YgY2x1c3RlciAyOSwgYnV0IGl0IGRvZXMgY29uc2lzdGVudGx5IGV4cHJlc3MgU0NUIG1hcmtlcnMgZnJvbSB2ZW50byBhbmQgc3VyeWEuCjMuIENsdXN0ZXIgMjQgZG9lc24ndCBleHByZXNzIEVSVkZSRC0xIG11Y2gsIGJ1dCBpdCBkb2VzIGV4cHJlc3MgbW9zdCBvdGhlciB0ZWxsdGFsZSBnZW5lcyBvZiBTQ1Q6IENTSDEsIENTSDIsIENHQSwgQ1NITDEsIG1hbnkgUFNHIGdlbmVzLCBHSDIsIFBHRiwgR0RGMTUsIGV0Yy4gQW5kIGl0IGlzIG5vdCBlbnJpY2hlZCBmb3IgZXhwcmVzc2lvbiBvZiBWQ1QgbWFya2VycyBmcm9tIHZlbnRvIGFuZCBzdXJ5YS4gSXQncyBlbnJpY2hlZCBHTyB0ZXJtcyBoYXZlIHRvIGRvIHdpdGggaG9ybW9uZSBwcm9kdWN0aW9uIGFuZCBzaWduYWxpbmcgYXMgZXhwZWN0ZWQgZm9yIFNDVC4gVGh1cywgdGhpcyBpcyBtb3N0IGxpa2VseSBhbiBTQ1QgY2x1c3Rlci4gCjQuIENsdXN0ZXJzIDE2IGFuZCAxNyBkb24ndCBleHByZXNzIG1hbnkgbWFya2VycyBvZiBTQ1QsIGJ1dCBkbyBleHByZXNzIG1hbnkgdmVudG8gYW5kIHN1cnlhIG1hcmtlcnMgb2YgVkNUOiBQQUdFNCwgUEVHMTAsIFhBR0UzLCBldGMuCjUuIENsdXN0ZXIgMjUgaGFzIFZDVCBtYXJrZXJzIGFzIHdlbGwgYXMgYSBzdHJvbmcgc2lnbmF0dXJlIG9mIGNlbGwgZGl2aXNpb24gZ2VuZXMuIFRoaXMgc2hvd3MgdXAgaW4gdGhlIEdPIGVucmljaG1lbnQgYXMgd2VsbCwgd2hlcmUgbW9zdCBlbnJpY2hlZCBHTyB0ZXJtcyBhcmUgIm51Y2xlYXIgZGl2aXNpb24iLCAiY2hyb21vc29tZSBzZWdyZWdhdGlvbiIsICJjZWxsIGN5Y2xlIEcxL1MgcGhhc2UgdHJhbnNpdGlvbiIgZXRjLiBUaGlzIGlzIGxpa2VseSBhIGNsdXN0ZXIgb2YgcHJvbGlmZXJhdGluZyBWQ1QgY2VsbHMuIEluY2lkZW50YWxseSBib3RoIFtAdmVudG8tdG9ybW9fc2luZ2xlLWNlbGxfMjAxOF0gYW5kIFtAbGl1X3NpbmdsZS1jZWxsXzIwMThdIGhhdmUgYSBzZXBhcmF0ZSBwb3B1bGF0aW9uIG9mIFZDVCB3aXRoIGEgY2VsbCBjeWNsZSBnZW5lIHNpZ25hdHVyZSAoVkNUXzIgaW4gdmVudG8pLiAKNi4gQ2x1c3RlciAyMyBpcyBtb3N0IHNpbWlsYXIgdG8gU0NUIG9mIHZlbnRvIGJ1dCBWQ1Qgb2Ygc3VyeWEuIEluZGVlZCB0aGlzIGNsdXN0ZXIgaGFzIGV4cHJlc3Npb24gb2YgbWFya2VyIGdlbmVzIGZvciBib3RoIFZDVCBhbmQgU0NUIChzZWUgcGxvdHMpLiBJdCBleHByZXNzZXMgUEFHRTQsIFBFRzEwIGV0YywgYnV0IGFsc28gZXhwcmVzc2VzIHN5bmN5dGluIGdlbmUgRVJWVy0xLiBUaGlzIGNvdWxkIHJlcHJlc2VudCBhIGdyb3VwIG9mIGN5dG90cm9waG9ibGFzdCBjZWxscyB0aGF0IGFyZSBpbiB0aGUgcHJvY2VzcyBvZiBkaWZmZXJlbnRpYXRpbmcgdG8gc3ljbnl0aWFsIHRyb3Bob2JsYXN0LiBXZSB3aWxsIGxhYmVsIGl0IGFzIGEgVkNUIHR5cGUuIAoKYGBge3J9CmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8xNiIpXSA8LSAidmlsLlZDVF8xIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMTciKV0gPC0gInZpbC5WQ1RfMiIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzIzIildIDwtICJ2aWwuVkNUXzMiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8yNSIpXSA8LSAidmlsLlZDVF80Igphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMjQiKV0gPC0gInZpbC5TQ1RfMSIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzI5IildIDwtICJ2aWwuU0NUXzIiCgphbm5vdGF0aW9uJG5vdGVzW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzI1IildIDwtICJwcm9saWZlcmF0aW5nIgoKYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBwYXN0ZTAoImNsdXN0XyIsIGMoIjE2IiwgIjE3IiwgIjIzIiwgIjI1IiwgIjI0IiwgIjI5IikpLCBdCmBgYAoKIyMjIE1hcmtlciBnZW5lIHBsb3RzCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8xNiJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8xNyJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8yNSJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8yMyJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8yNCJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8yOSJdXQpgYGAKCiMjIyBHTyBlbnJpY2htZW50CmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMTYiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMTciKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMjUiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMjMiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMjQiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMjkiKQpgYGAKCiMjIyBWZW50byBtYXJrZXJzIGZvciBWQ1QgYW5kIFNDVApgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJFZDVF8xLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBWQ1RfMSIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRWQ1RfMiwgdGl0bGUgPSAidmVudG8gbWFya2VyczogVkNUXzIiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kU0NULCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBTQ1QiKQpgYGAKCiMjIyBTdXJ5YSBtYXJrZXJzIGZvciBWQ1QgYW5kIFNDVApgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJ2aWwuVkNUIl0sIHRpdGxlID0gInN1cnlhIG1hcmtlcnM6IHZpbC5WQ1QiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMuc3VyeWEkZ2VuZVttYXJrZXJzLnN1cnlhJGNsdXN0ZXIgPT0gInZpbC5TQ1QiXSwgdGl0bGUgPSAic3VyeWEgbWFya2VyczogdmlsLlNDVCIpCmBgYAoKIyMjIFBhdmxpIG1hcmtlciBnZW5lcyBmb3IgVkNUIGFuZCBTQ1QKYGBge3IgZmlnLmFzcD0wLjQsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gYygiQURSQjEiLCAiUFVGNjAiLCAiU05PUkRBM0EiLCAiUFJNVDciLCAiRTFGMUFZIiwgCiAgICAgICAgICAgICAgICAgICAgICAiRlhZRDMiLCAiR1JBTUQyIiwgIklOU0w0IiwgIklUR0I4IiwgIlBBR0U0IiwKICAgICAgICAgICAgICAgICAgICAgICJTTEMxM0E0IiwgIlNMQzIyQTExIiksIAogICAgICAgICB0aXRsZSA9ICJwYXZsaSBtYXJrZXJzOiBWQ1RfMSIpCmBgYAoKCmBgYHtyIGZpZy5hc3A9MC42LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IGMoIlhBR0UzIiwgIlhBR0UyIiwgIlNFUklOQzUiLCAiUzEwMFAiLCAiUkFTQTEiLAogICAgICAgICAgICAgICAgICAgICAgIk1GQVA1IiwgIkxJTjI4QiIsICJLUlQ4IiwgIktSVDciLCAiSU5TLUlHRjIiLCAiR0NNMSIsCiAgICAgICAgICAgICAgICAgICAgICAiR0FUQTMiLCAiRVJWVy0xIiwgIkVSVkZSRC0xIiwgIkVHUjEiLCAiRUZOQTEiLCAKICAgICAgICAgICAgICAgICAgICAgICJDWVAxOUExIiwgIlBITERBMiIsICJBQ0tSMiIpLCAKICAgICAgICAgdGl0bGUgPSAicGF2bGkgbWFya2VyczogVkNUXzIiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IGMoIlNFTUEzQiIsICJDU0gyIiwgIkNTSEwxIiwgIkZDR1IyQSIsICJHSDIiLCAiSEJBMSIsIAogICAgICAgICAgICAgICAgICAgICAgIkhCQTIiLCAiSEJCIiwgIkhCRzEiLCAiSEJHMiIsICJITEEtRE1BIiwgIkhMQS1EUEExIiwKICAgICAgICAgICAgICAgICAgICAgICJITEEtRFFCMSIsICJITEEtRFJBIiwgIkhMQS1EUkIxIiwgIkhQR0RTIiwgIkNHQjgiLAogICAgICAgICAgICAgICAgICAgICAgIkxHQUxTMTQiLCAiTFlWRTEiLCAiTlBJUEIzIiwgIkNHQjUiLCAiUFNHMSIsICJQU0czIiwgCiAgICAgICAgICAgICAgICAgICAgICAiUFNHNiIsICJQU0c5IiwgIlpORjExNyIsICJaTkY5MSIsICJISVNUMkgyQUIiLAogICAgICAgICAgICAgICAgICAgICAgIlJBTVAyIiwgIk1VQzIwIiksIAogICAgICAgICB0aXRsZSA9ICJwYXZsaSBtYXJrZXJzOiBTQ1QiKQpgYGAKCiMjIyBMaXUgZXQgYWwgbWFya2VycyBmb3IgdHJvcGhvYmxhc3RzCmBgYHtyIGZpZy5hc3A9MC41fQpEb3RQbG90MihzZXVyLCBmZWF0dXJlcyA9IGMoIkdDTTEiLCAiQ1NIMSIsICJLUlQ3IiwgIlRGQVAyQyIsICJHQVRBMyIsICJDREgxIiwgIkVHRlIiLCAiSExBLUciLCAiTU1QMiIsICJSUk0yIiwgIkNDTkIxIiwgIkNESzEiLCAiRVJWRlJELTEiLCAiU0xDMUE1IiwgIlBBR0U0IiwgIkNHQiIpLCB0aXRsZSA9ICJHZW5lcyBmcm9tIExpdSBldCBhbCAyMDE4IikKYGBgCiMjIENsdXN0ZXJzIDAyLCAwNiwgMDcsIDE1LCAxOSwgMzMKIyMjIE5vdGVzCgoxLiBNb3N0IG9mIHRoZXNlIGNsdXN0ZXJzIHJlcHJlc2VudCBzb21lIGtpbmQgb2YgZmlicm9ibGFzdCBvciBhIGNsb3NlbHkgcmVsYXRlZCBjZWxsIHR5cGUuIE1vc3QgY2x1c3RlcnMgYXJlIGVucmljaGVkIGZvciBHTyB0ZXJtcyByZWxhdGVkIHRvIEVDTSByZWd1bGF0aW9uLiAKMi4gQ2x1c3RlcnMgMDIgYW5kIDE1IG1hcmtlciBnZW5lcyBhcmUgYWxzbyBoaWdobHkgZXhwcmVzc2VkIGluIGNsdXN0ZXJzIDAwIGFuZCAxOC4gQ2x1c3RlciAxNSBoYXMgbW9yZSB0aGFuIDc1JSBjZWxscyBjb21pbmcgZnJvbSBkZWNpZHVhIHNhbXBsZXMsIGFuZCBjbHVzdGVyIDAyIGhhcyBvdmVyIDUwJSBjZWxscyBjb21pbmcgZnJvbSBkZWNpZHVhIHNhbXBsZXMuIERlY2lkdWFsIHN0cm9tYWwgY2VsbHMgYXJlIGtub3duIHRvIGJlIGFjdHVhbGx5IGEgZmV3IGRpZmZlcmVudCBwb3B1bGF0aW9ucy4gRXZlbiBpbiBWZW50by10b3JtbyBldCBhbCwgdGhleSBhcmUgbGFiZWxlZCBhcyBkUzEtLTMsIG9mIHdoaWNoIGRTMyBhcmUgdGhlIGNhbm9uaWNhbCBEU0Mgd2l0aCBQUkwgYW5kIElHRkJQMSwgd2hpbGUgZFMxIGFuZCBkUzIgYXJlIGNsb3NlbHkgcmVsYXRlZCB0byBEU0MgYW5kIGFyZSBkZXJpdmVkIGZyb20gZW5kb21ldHJpYWwgc3Ryb21hbCBmaWJyb2JsYXN0cy4gVGhlc2UgdGhpbmdzIHRvZ2V0aGVyIHN1Z2dlc3QgdGhhdCBjbHVzdGVycyAwMiBhbmQgMTUgYXJlIHRoZSBub24tUFJMIHZhcmlldGllcyBvZiBEU0MuIFRvIGRpc3Rpbmd1aXNoIHRoZW0gZnJvbSBEU0MsIHdlIGNhbiBjYWxsIHRoZW0gZGVjaWR1YWwgZmlicm9ibGFzdHMuIAozLiBNYXJrZXIgZ2VuZXMgb2YgMDYsIDA3LCBhbmQgMzMgc2hvdyBoaWdobHkgY29ycmVsYXRlZCBnZW5lIGV4cHJlc3NpbiwgaS5lLiB0aGUgbWFya2VycyBvbiBlYWNoIG9mIHRoZXNlIGNsdXN0ZXJzIGFyZSBhbHNvIGhpZ2hseSBleHByZXNzZWQgaW4gdGhlIG90aGVyIHR3byBjbHVzdGVycywgc3VnZ2VzdGluZyB0aGF0IHRoZXNlIHRocmVlIGNsdXN0ZXJzIGEgY2xvc2VseSByZWxhdGVkIGNlbGwgcG9wdWxhdGlvbnMuIEFsbCB0aHJlZSBvZiB0aGVzZSBjbHVzdGVycyBhcmUgbGFyZ2VseSAob3ZlciA3NSUgb2YgdGhlIGNlbGxzKSBkZXJpdmVkIGZyb20gdmlsbGkgc2FtcGxlcywgYW5kIHRoZXkgYXJlIGZpYnJvYmxhc3QtbGlrZSBjZWxscy4gVGh1cywgdGhlc2UgYXJlIGxpa2VseSB2aWxsb3VzIGZpYnJvYmxhc3RzLgo0LiBDbHVzdGVyIDE5IGlzIG1vc3Qgc2ltaWxhciB0byBQViAocGVyaXZhc2N1bGFyKSBjbHVzdGVycyBmcm9tIHZlbnRvIGFuZCBTTUMgKHNtb290aCBtdXNjbGUgY2VsbCkgY2x1c3RlciBmcm9tIHN1cnlhLiBJbmRlZWQsIGl0cyBtYXJrZXJzIGluY2x1ZGUgc21vb3RoIG11c2NsZSBnZW5lcyBsaWtlIE1HUCwgTVlIMTEsIE1ZTDkgZXRjLCBhbmQgZW5yaWNoZWQgR08gdGVybXMgZm9yIHRoaXMgY2x1c3RlciBpbmNsdWRlICJtdXNjbGUgY29udHJhY3Rpb24iIGFuZCAiYXJ0ZXJ5IG1vcnBob2dlbmVzaXMiLiBUaHVzLCB0aGlzIGNsdXN0ZXIgaXMgbW9zdCBsaWtlbHkgb2YgcGVyaXZhc2N1bGFyIHNtb290aCBtdXNjbGUgY2VsbHMgb2YgZGVjaWR1YWwgb3JpZ2luIChvdmVyIDc1JSBjZWxscyBmcm9tIGRlY2lkdWEgc2FtcGxlcykuIAoKYGBge3J9CmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8wMiIpXSA8LSAiZGVjLkZCXzEiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8xNSIpXSA8LSAiZGVjLkZCXzIiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8wNiIpXSA8LSAidmlsLkZCXzEiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8wNyIpXSA8LSAidmlsLkZCXzIiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8zMyIpXSA8LSAidmlsLkZCXzMiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8xOSIpXSA8LSAiZGVjLlNNQyIKCmFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgcGFzdGUwKCJjbHVzdF8iLCBjKCIwMiIsICIxNSIsICIwNiIsICIwNyIsICIzMyIsICIxOSIpKSwgXQpgYGAKCiMjIyBNYXJrZXIgZ2VuZSBwbG90cwpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMDIiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMTUiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMDYiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMDciXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMTkiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMzMiXV0KYGBgCgojIyMgR08gZW5yaWNobWVudApgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzAyIikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzE1IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzA2IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzA3IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzE5IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzMzIikKYGBgCgojIyMgVmVudG8gbWFya2VycwoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRQVjEsIHRpdGxlID0gInZlbnRvIG1hcmtlcnM6IFBWMSIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRQVjIsIHRpdGxlID0gInZlbnRvIG1hcmtlcnM6IFBWMiIpCmBgYAoKIyMjIFN1cnlhIG1hcmtlcnMKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy5zdXJ5YSRnZW5lW21hcmtlcnMuc3VyeWEkY2x1c3RlciA9PSAidmlsLkZCMSJdLCB0aXRsZSA9ICJzdXJ5YSBtYXJrZXJzOiB2aWwuRkIxIikKYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJkZWMuU01DIl0sIHRpdGxlID0gInN1cnlhIG1hcmtlcnM6IGRlYy5TTUMiKQpgYGAKCiMjIENsdXN0ZXIgMjAKIyMjIE5vdGVzClRoaXMgaXMgbW9zdCBsaWtlbHkgYSBjbHVzdGVyIG9mIEIgY2VsbHMuIE1vc3QgbWFya2VycyBnZW5lcyBhcmUgcmVsYXRlZCB0byBCIGNlbGxzOiBtYW55IGltbXVub2dsb2J1bGluIGduZWVzLCBTUElCLCBNUzRBMSwgQ0Q3OUEsIENENzlCLCBCQU5LMSwgZXRjLiBJdCBhbHNvIGV4cHJlc3NlcyBtYW55IGdlbmVzIHRoYXQgYXJlIG1hcmtlcnMgZm9yIFBsYXNtYSBjbHVzdGVyIGZyb20gdmVudG8gZGF0YXNldC4gR08gZW5yaWNobWVudCBhbHNvIGFncmVlcy4gRXZlbiB0aG91Z2ggYWJvdXQgaGFsZiB0aGUgY2VsbHMgZnJvbSB0aGUgY2x1c3RlciBjb21lIGZyb20gZGVjaWR1YSBhbmQgdmlsbGkgZWFjaCwgdGhlIGxpa2VseSBvcmlnaW4gb2YgQiBjZWxscyBpcyBtYXRlcm5hbCByYXRoZXIgdGhhbiBmZXRhbC4gCgpgYGB7cn0KYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzIwIildIDwtICJkZWMuQmNlbGxzIgoKYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBwYXN0ZTAoImNsdXN0XyIsIGMoIjIwIikpLCBdCmBgYAoKIyMjIE1hcmtlciBnZW5lcyBwbG90CmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8yMCJdXQpgYGAKCiMjIyBHTyBlbnJpY2htZW50CmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMjAiKQpgYGAKCiMjIyBWZW50byBtYXJrZXJzCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kUGxhc21hLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBQbGFzbWEiKQpgYGAKCgojIFNlc3Npb24gSW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCiMgUmVmZXJlbmNlcwoK